美文网首页
runtime关联值,为分类添加属性

runtime关联值,为分类添加属性

作者: 2700a4b1aed1 | 来源:发表于2018-03-16 18:47 被阅读0次

    在iOS开发中为了方便开发,通常我们会通过Category对系统的类进行扩展,给系统类添加方法,例如

    @interface UIImage (Image)
    /** 根据颜色生成一张1*1的纯色图片*/
    + (UIImage *)imageWithColor:(UIColor *)color;
    @end
    #import "UIImage+Image.h"
    
    @implementation UIImage (Image)
    + (UIImage *)imageWithColor:(UIColor *)color
    {
        CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
        UIGraphicsBeginImageContext(rect.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(context, [color CGColor]);
        CGContextFillRect(context, rect);
        UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return theImage;
    }
    @end
    

    给UIImage添加一个分类,可通过传入颜色直接生成一张颜色对应的纯色图片
    这样的功能主要就是OC的runtime机制实现的.
    但是在category中是不能够直接去添加属性的,因为系统是不会为我们自动的去生成属性对应的setter/getter方法

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImageView setTestStr:]: unrecognized selector sent to instance 0x7fa32e509310'
    

    直接添加之后赋值的时候报错
    category为什么会有这样的限制呢?我们需要从@property关键字声明属性默认做的事情来思考

    @interface UIImageView (rumtime)
    @property (nonatomic, strong) NSString *testStr;
    @end
    

    我们在UIImageView的category中声明了一个NSString属性
    正常情况下,在.h中声明属性后,我们就可在类的实例中使用这些属性了.为了能够正确使用属性,OC会默认为我们完成以下工作
    在.m中,编译器通过@synthesize关键字,将我们声明的属性转换为了对应的实例变量,并默认在属性名称前添加'_'来在类中标识对应的实例变量. 根据我们在@property中指定的访问限制(readwrite/readonly),编译器会自动生成默认的setter/getter方法(其本质仍是对带下划线的实例变量的操作).(如果是readonly,则只会生成getter方法).由@property生成的实例变量,会在类实例创建时(alloc)被分配内存,类实例销毁时释放内存.
    @property关键之相关内容请查看

    https://www.jianshu.com/p/e1c4c45ebd96

    @synthesize与生成setter/getter方法,均是在编译期完成的,而category则属于runtime时期加载,所以编译器就不会为我们做@synthesize与生成对应setter/getter方法的工作了,因此我们也就不能够在category中添加属性,直接添加之后实例对象访问时直接报错.

    所以我们需要使用runtime动态的去关联属性

    话不多数,直接上代码

    @interface UIImageView (rumtime)
    @property (nonatomic, strong) NSString *testStr;
    @end
    
    #import "UIImageView+rumtime.h"
    #import <objc/runtime.h>
    static char *imageViewClickKey;
    
    @interface UIImageView ()
    @end
    @implementation UIImageView (rumtime)
    
    - (void)setTestStr:(NSString *)testStr {
        objc_setAssociatedObject(self, @selector(testStr), testStr,  OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSString *)testStr {
        return objc_getAssociatedObject(self, @selector(testStr));
    }
    
    @end
    

    需要注意的是,建立关联的key可以为任意值,但是必须保证其唯一性,并且在setter和getter中均能访问,而@selector正好满足,故我们可以使用它作为key.添加的属性应为NSObject的子类,一般属性如int等不能设置关联.对于OBJC_ASSOCIATION_ASSIGN,虽然是weak引用,但其并不会像property的weak那样释放后自动为nil,而是一个野指针,这里要注意不要引发BAD ACCESS异常。

    文章参考了https://www.2cto.com/kf/201609/545734.html

    相关文章

      网友评论

          本文标题:runtime关联值,为分类添加属性

          本文链接:https://www.haomeiwen.com/subject/fhgtxftx.html