面试题:Category能否添加成员变量?如果可以,如何给Category添加?
不能直接给Category添加成员变量,但是可以通过runtimeAPI objc_setAssociatedObject间接实现添加成员变量的效果。
-------------------------------------------------------------------------------------------
在分类中添加属性:如下
其中@property(assign, nonatomic) int weight; 和@property(copy, nonatomic) NSString *name;并没有实现添加成员变量和set、get方法。
所以可以在.m文件中自己去实现:
方法1:
因为没有实现添加成员变量,所以直接按照原类中实现的set,get方法会报错
所以我们可以通过定义一个全局变量来装载,如下:
但是这样出现的结果是weight因为存放在全局的变量中,所有的对象调用都能对它的值进行更改覆盖,达不到我们想要这种多个类对象赋值一一对应的关系。打印如下:
方法2:
因此有了上一方法的结果,想到可以使用字典一一对应的关系的存储。实现如下
通过初始化一个字典来,将当前的类地址作为key值,传进来的数据key作为value存储在字典中,运行结果似乎也是达到了效果。
方法3:
从方法2中也可以看出来添加一个属性,后面m文件中要实现的代码比较多,并且考虑到线程安全,比如当多个线程同时在调用赋值取值,可能会造成不安全的情况,所以oc的runtimeAPI中有objc_setAssociatedObject方法,对它进行优化才有方法3。
其中全局变量加上static修饰 目的是为了只让在当前文件内更改有用,不让在外面随意更改,私有化。
可以看出我们传入的是当前变量的地址,至于为什么是const void *格式,这是由于底层方法就是赋予了这样的类型(我们也可以传入int型,char型,当然相对int型,char型,更好的是char,因为int占据4个字节,char才一个字节)。我们也可以直接传入字符串如下:
方法4:
经过优化,最终在项目中我们这样用得比较多,将传入的第二个参数换成@selector(name),当然在get方法中可以再次换成_cmp,因为get方法其实有隐式参数,方法名-(NSString)name;等价于-(NSString)name:(id)self _cmd:(SEL)_cmd;
默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中,但可以通过关联对象来间接实现。
关联对象提供了以下API:
添加对象:void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
获取关联对象:id objc_getAssociatedObject(id object, const void *key);
移除所有的关联对象:void objc_removeAssocatedObjects(id object)
关联对象原理
关联对象并不是存储在被关联对象本身内存中
关联对象存储在全局的统一的一个AssociationManager中
如果关联对象为nil,就相当于移除关联对象
网友评论