美文网首页
OC Category 的几个点

OC Category 的几个点

作者: Gxdy | 来源:发表于2018-09-04 11:29 被阅读0次

    Category 的结构引申

    附加:Category 的处理过程

    1. Xcode在编译时,会将所有参与编译的分类转变成一个c++结构体(图1所示)


      图片 1.png
    2. 运行时通过runtime加载所有category数据,并将其方法、属性、协议数据按照编译顺序,分别合并到一个对应的大数组
      [后编译的在数组的前面] ----> 这样方法调用时就会优先调用后编译的分类方法
    3. 将合并后的分类数据(方法、属性、协议),插入到类(类对象、元类对象)原来数据的前面 ----> 优先调用分类方法

    综上概括:

    1. 本质上所有对象方法、协议、属性最终都会按照分类在前,原类在后;后编译的分类在前,先编译在后的原则,分别存放在类对象对象方法、协议、属性对应的数组里面
    2. 所有类方法最终都会按照分类在前,原类在后;后编译的分类在前,先编译在后的原则,存放在元类对象类方法数组里面

    结论

    1. 最终方法或属性等都一个数组,且有序,所以就有了方法调用顺序:后编译的分类方法-->先编译的分类方法-->类对象(元类对象)原来的方法
    2. 因为程序运行时runtime是把所有分类数据合并到原类所有数据中,所以不管有没有import 某个分类的头文件,这个分类中 重写的方法都会可能被调用
    3. 分类结构体中包含有属性数组``struct property_list *instanceProperties;,所以分类允许添加属性的,但需要手动实现getter 和setter(@proterty... 在分类声明属性,只会生成属性的getter 和setter声明,但不会实现,和生成带_的成员变量
    4. 分类结构体中不含有成员变量数组``struct property_list *instanceProperties;,可见分类不存在成员变量,也不能添加成员变量



    拓展:如何给分类添加成员变量

    1. 上述结论中谈到:由于分类结构限制,分类不存在成员变量,也不能添加成员变量,但是我们可以用伪成员变量实现类似效果的
    1. 实现方案:
      a. 在分类interface用中@property声明一个属性
      b. 在分类implementation中实现属性的 getter 和setter。由于分类不能添加和生成成员变量,所以最常用且可靠的方案是通过Runtime API关联一个伪成员变量,来实现getter 和setter方法
    1. 关联对象的相关Runtime API
    // 添加关联对象
    void objc_setAssociatedObject(id object, 
                                  const void * key,
                                  id value,
                                  objc_AssociationPolicy policy)
    //  获得关联对象
    id objc_getAssociatedObject(id object, const void * key)
    // 移除所有的关联对象
    void objc_removeAssociatedObjects(id object)
    
    /** 参数说明:
     *  object :id类型, 一般都传 self;用途相当于一个字典的Key,
                用来归类所关联的成员变量; 
     *  key :const void * 类型指针,相当于读写属性值的Key,
             可以传用@selector(getter) --》 [这样能免除繁琐的宏或常量定义]
     *  value : 成员变量的值 (对象)
     *  objc_AssociationPolicy policy : 内存策略,相当于retain、strong、copy等
    
    1. 示例
    #import <objc/runtime.h>
    
    @interface TLPerson (ExampleCode)
    
    @property (copy, nonatomic) NSString *name;
    @property (assign, nonatomic) int weight;
    @end
    
    @implementation TLPerson (ExampleCode)
    
    - (void)setName:(NSString *)name
    {
        /* policy 参数类型
        typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,          
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,                                            
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
        OBJC_ASSOCIATION_RETAIN = 01401,      
        OBJC_ASSOCIATION_COPY = 01403      
        };*/
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    - (NSString *)name
    {
        // 隐式参数
        // _cmd == @selector(name),
        // 用@selector(name)作为key,可以简化代码,省去一些宏定义,还有能有代码提示
        return objc_getAssociatedObject(self, _cmd);
    }
    
    - (void)setWeight:(int)weight
    {
        objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (int)weight
    {
        // _cmd == @selector(weight)
        return [objc_getAssociatedObject(self, _cmd) intValue];
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:OC Category 的几个点

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