Category

作者: 纳兰沫 | 来源:发表于2019-12-02 14:03 被阅读0次

    Category的使用场合是什么

    拆解模块
    

    Category的实现原理

    Category的底层结构是struct category_t 里面存储着分类的对象方法 类方法 属性 协议
    在程序运行的时候 runtime会将Category的数据 合并到类信息中(类对象、元类对象中)
    

    Category 和 Extension的区别是什么

    Class Extension 在编译的时候 他的数据就已经包含在类信息中
    Category 是在运行时 才会将数据合并到类信息中
    

    Category有load方法吗? load方法是什么时候调用的? load方法能继承吗?

    有load方法
    load方法在runtime加载类 分类的时候调用
    load方法可以继承 但是一般情况下 不会主动去调用load方法 都是让系统自动调用
    

    load initialize方法的区别是什么 他们在category中的调用顺序是什么 以及出现继承的时候他们的调用过程

    区别
    1.调用方式
      a.load是根据函数地址直接调用
      b.initialize是通过objc_msgSend调用
    2.调用时刻
      a.load是runtime加载类 分类的时候调用
      b.initialize是类第一次接受到消息的时候调用 每一个类只会initialize一次 (父类的initialize方法可能会被调用多次)
    调用顺序
    load
     - 先调用类的load
       a.先编译的类 优先调用load
       b.调用子类的load之前 会先调用父类的load
     - 再调用分类的load
       a.先编译的分类 优先调用load
    initialize
     -  先初始化父类
     -  再初始化子类 (可能最终调用的是父类的initialize方法)
    

    Category能否添加成员变量 如果可以 如何给Category添加成员变量

    不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果
    

    Catgory的底层结构

    Catgory的底层结构.png
    - 编译时 所有分类的数据都会存放在_category_t的结构体中 
    - 一个分类就相当于一个结构体 有多少分类就有多少对应的_category_t结构体
    - 运行时 通过runtime动态将分类的方法合并到类对象 元类对象中
    - 最后参与编译的分类 放在最前面 他的方法在方法列表的最前面 最先被调用
    

    Category的加载处理过程

    1.通过Runtime加载某个类的所有Category数据
    2.把所有Category的方法 属性 协议数据 合并到一个大数组中 
      后面参与编译的Category数据 会在数组的前面(先对数组进行扩容 原先的大小+分类里面的列表的大小 然后把原先
    放在方法列表的里面往后挪动,挪动到最后 把前面的位置空出来,把分类里面的方法列表放在原先的方法列表的前面)
    3.将合并后的分类数据 (方法 属性 协议) 插入到原来数据的前面
    **最后编译的放在最前面 所以 会被先调用**
    

    一个类只有一个类对象

    memove memcopy区别

    - memove 根据传进去的参数先判断往哪个方向挪动 判断挪数据是哪个数据先开始挪动(可以保证数据可以完整的挪动过去)
    - memcopy 一个个的拷贝 从小地址开始 一个个的挪动
    

    load方法

    不管这个类是否被调用 都会加载这个load方法

    +load 方法会在runtime加载类 分类时调用
    每个类、分类的+load,在程序运行过程中只调用一次
    
    load方法调用顺序
    1.先调用类的+load
      按照编译先后顺序调用(先编译,先调用)
      调用子类的+load之前会先调用父类的+load
    2.再调用分类的+load
      按照编译先后顺序调用(先编译,先调用)
    

    load方法的调用是根据方法地址直接调用 不是经过objc_msgSend函数调用

    initialize方法

    initialize方法 是类第一次接受到消息的时候调用
    
    initialize方法调用顺序
    - 先调用父类的+initialize,再调用子类的+initialize
    - (先初始化父类,再初始化子类,每个类只会初始化1次)
    

    如果分类实现了+ initialize 方法 就会覆盖类本事的initialize 调用
    如果子类没有实现initialize 会调用父类的initialize (所以 父类的initialize可能会被调用多次)

    initialize会先调用父类的initialize方法 是因为它内部有主动调用父类的initialize(有继承关系和父类还没有调用initialize方法的前提下)

    关联对象

    使用全局变量实现setter方法
    缺点 因为全局变量的原因 会造成所有的对象都是一个属性值 值是一样的
    
    使用全局的字典实现setter方法
    缺点 线程安全 内存泄露问题  不同的线程访问字典出现线程安全
    如果出现多个属性 就要出现多个字典 代码复杂
    

    设置关联对象

    设置key为const void *MJNameKey;

    因为未设置值 就代表key是NULL 多个key都是NULL 出现相应问题

    const void *MJNameKey;
    
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, MJNameKey);
    }
    
    设置key为const void *MJNameKey = &MJNameKey;

    未设置为static 外部可以访问改变

    const void *MJNameKey = &MJNameKey;
    
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, MJNameKey);
    }
    
    设置key为static const void *MJNameKey = &MJNameKey;

    使得该key只能在当前文件使用

    static const void *MJNameKey = &MJNameKey;
    
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, MJNameKey);
    }
    
    设置key为static const char MJNameKey;

    只占用一个字节 节省空间

    static const char MJNameKey;
    
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, &MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, &MJNameKey);
    }
    
    设置key为@"name"

    字符串里面装的是地址值

    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, @"name");
    }
    
    设置key为宏定义
    #define MJNameKey @"name"
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, MJNameKey);
    }
    
    设置key为IMP
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, @selector(name));
    }
    
    设置key为IMP 同时在getName里面设置为_cmd
    - (void)setName:(NSString *)name{
    
        //id  _Nonnull object 对象 关联对象
        //const void * _Nonnull key  指针
        //id  _Nullable value  关联的值
        //objc_AssociationPolicy policy  关联策略
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    -(NSString *)name{
    
        return objc_getAssociatedObject(self, _cmd);
    }
    

    关联策略

    关联策略.png

    关联对象的原理

    关联对象的核心对象
    AssociationsManager
    AssociationsHashMap
    ObjectAssociationMap
    ObjcAssociation
    
    关联对象的核心对象.png 关联对象的核心对象之间的关系.png
    void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    
    其中object 是AssociationsHashMap的key 也就是disguised_ptr_t
    key存放在ObjectAssociationMap里面 是里面的key
    value  policy 是在ObjcAssociation里面存放
    

    关联对象并不是存储在被关联对象本身内存中 关联对象存储在全局的统一的一个AssociationsManager中
    设置关联对象为nil,就相当于是移除关联对象

    相关文章

      网友评论

          本文标题:Category

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