美文网首页
底层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