美文网首页
底层6:为Category间接添加成员变量

底层6:为Category间接添加成员变量

作者: 张无奈 | 来源:发表于2020-08-30 00:27 被阅读0次

面试题: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,就相当于移除关联对象

相关文章

网友评论

      本文标题:底层6:为Category间接添加成员变量

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