美文网首页
OC分类Category

OC分类Category

作者: 芝麻酱的简书 | 来源:发表于2019-01-15 15:50 被阅读9次

    分类Category

    1.分类的应用:
    • 声明私有方法
    • 分解体积庞大的类文件
    • 把Framework的私有方法公开
    2.分类的特点(或者说分类跟扩展的区别):
    • 运行时决议:在编写完分类文件后,并没有直接把分类内容添加到相关的宿主类上。而是在运行时使用runtime把分类的内容添加到宿主类上。
    • 可以为系统类添加分类
    3.分类的优、缺点:

    优点:

    • 不需要通过增加子类而增加现有类的行为(方法),且类目中的方法与原始类方法基本没有区别;

    • 通过类目可以将庞大一个类的方法进行划分,从而便于代码的日后的维护、更新以及提高代码的阅读性;

    缺点:

    • 无法向类目添加实例变量,如果需要添加实例变量,只能通过定义子类的方式;

    • 类目中的方法与原始类以及父类方法相比具有更高优先级,如果覆盖父类的方法,可能导致super消息的断裂。因此,最好不要覆盖原始类中的方法。

    4.分类加载流程:

    在运行APP的时候,加载完动态链接库,会加载可执行文件,通过runtime生成类、成员变量、方法列表,在宿主类的方法列表生成完之后,会开始加载分类的方法列表。

    取到所有分类的列表数组(按编译时的顺序排序),然后按倒序从分类列表里取出每个分类的方法列表,生成一个二维数组。

    把二维数组中的方法,按正序即从0索引开始,放入到宿主类的方法列表中(一维的数组)。
    所以分类才拥有了“覆盖”原有类的方法功能,其实是原方法是存在的,只是分类的方法列表加入到了原有类的方法数组的前边,获得优先执行权。

    5.分类中都可以添加哪些内容:
    struct category_t {
        const char *name;                           分类名称
        classref_t cls;                             该分类所属的宿主类
        struct method_list_t *instanceMethods;      实例方法列表
        struct method_list_t *classMethods;         类方法列表
        struct protocol_list_t *protocols;          协议列表
        struct property_list_t *instanceProperties; 实例属性列表
        
        ... 省略
    }
    

    在编译时候,分类会被编译成一个category_t的机构体,储存如下信息。

    • 增加实例方法
    • 增加类方法
    • 增加协议
    • 增加实例属性。在分类中定义了一个属性,实际上只声明了对应的get方法和set方法,并没有在分类中添加实例变量var。
    • 增加实例变量。⚠️使用 runtime关联对象 技术来添加。
    6.分类添加过程:

    在程序运行时候,runtime会把分类的实例方法等信息合并到类对象的实例方法列表中,会把分类的类方法合并到元类对象的类方法列表中。

    以添加实例方法为例:
    运行时候,会遍历分类列表,拿到每一个分类的实例方法列表

    分类1的实例方法列表 array1 [method_t, method_t]
    分类2的实例方法列表 array2 [method_t, method_t]
    

    然后根据获取到的所有分类的实例方法列表数 和 原宿主类的实例方法列表,重新分配内存,数据结构为一个新的二维数组

     new_array [所有分类的实例列表数目 + 宿主类的实例列表数][]
    

    最后把宿主类的实例列表移动都数组后面,并把所有分类的实例列表加入到新申请的二维数组的前边

    [
    array1,
    array2.
    [原宿主类的实例方法列表]
    ]
    


    补充知识1

    关联对象技术: 可以给分类添加成员变量。
    #关联对象: 使用objc_setAssociatedObject函数可以给某个对象关联其他的对象。
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
     
    #获取关联的对象: 使用objc_getAssociatedObject函数可以通过键来取出某个对象的关联对象。
    id objc_getAssociatedObject(id object, const void *key)
     
    #移除关联的对象:使用objc_removeAssociatedObjects函数可以移除某个对象身上的所有关联的对象。
    void objc_removeAssociatedObjects(id object)
    
    注意:

    void objc_removeAssociatedObjects(id object)函数移除的是某个对象身上的所有关联的对象。
    objc没有给我们提供移除object身上单个关联对象的函数,所以,我们一般通过objc_setAssociatedObject函数传入nil来达到移除某个关联对象的目的。
    如下:
    void objc_setAssociatedObject(object, key, nil, policy);

    key:    要保证全局唯一,key与关联的对象是一一对应关系。必须全局唯一
    value:  要关联的对象。
    policy: 关联策略。有五种关联策略。
    OBJC_ASSOCIATION_ASSIGN   等价于   @property(assign)。
    OBJC_ASSOCIATION_RETAIN_NONATOMIC   等价于   @property(strong, nonatomic)。
    OBJC_ASSOCIATION_COPY_NONATOMIC   等价于   @property(copy, nonatomic)。
    OBJC_ASSOCIATION_RETAIN   等价于   @property(strong,atomic)。
    OBJC_ASSOCIATION_COPY   等价于   @property(copy, atomic)。
    

    问:关联对象技术给分类添加的成员变量放在了哪里?
    答:关联对象由AssociationsManager管理并在AssociationsHashMap存储。
    所有对象的关联内容都在同一个容器中。

    问:怎样清除某一个关联对象被关联的值?
    答:setAssociatedObject方法中value值设为nil即可。



    补充知识2

    扩展Extension

    1.一般用扩展做什么:

    • 声明私有属性。
    • 声明私有方法
    • 声明私有成员变量。

    2.拓展的特点(与分类区别):

    • 编译时决议
    • 只以声明的形式存在,多数情况下寄生于宿主类的.m实现文件中
    • 不能为系统类添加拓展


    相关文章

      网友评论

          本文标题:OC分类Category

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