美文网首页
Category 学习

Category 学习

作者: Tony17 | 来源:发表于2020-02-28 09:07 被阅读0次

    前言

    CategoryOC语言的基础特性之一,用于在不改变原有类结构的情况下扩展该类功能(主要是添加方法等)。

    Category 原理

    编译时生成Category_t的结构体,结构体中主要包括实例方法里列表、类方法列表、属性列表,在运行时把结构体中的信息合并到原类中。合并的方式为最后编译的分类信息在最前面,原类中的信息在最后面,所以如果多个分类都定义了同一个方法(名称相同),则会执行最后编译的那个分类中的方法 。往原类中添加方法的步骤为:

    1. 给原类的方法列表扩容
    2. 原方法后移
    3. 分类方法占用原方法的位置(按编译顺序来,最后编译的在最前面)
    category-struct.png

    Category中没有办法添加属性,是因为分类的结构体中没有成员变量字段的定义,而属性是需要创建成员变量和getset方法的,虽然Category中创建属性的时候也会自动创建getset方法, 但是不会实现它, 而且没有对应的成员变量作为值的载体。所以不能实现属性的功能。

    extension 和 Category 的区别

    • OC中的扩展没有名称(匿名),而且扩展的功能是分散原类头文件臃肿和定义不想对外曝露的部分。在编译时就会合并到原类中,所以可以添加属性,成员变量等信息。
    • 分类是对原类的扩展,由于不会改变原类结构和在运行时才会合并到原类信息中,所以不支持添加属性,成员变量等信息。

    Load 方法

    Load方法会在runtime加载类,分类的时候调用,每个类、分类在程序运行过程中只会调用一次。调用顺序为:

    1. 先调用父类,在调用子类
    2. 先调用原类,在按照编译顺序调用分类

    Load方法是可以继承的。

    Initialize 方法

    在类第一次接收到消息的时候调用。先调用父类的该方法,再调用子类的该方法。

    Initialize 是通过objc_msgSend调用的,特性如下:

    • 所以如果分类实现了Initialize方法,则只会调用分类的Initialize方法,不会再调用原类的Initialize方法。
    • 如果子类没有实现Initialize方法,会调用父类的Initialize方法。
    • 如果多个子类继承自同一个父类且子类都没有实现Initialize方法,则多个子类创建的时候会多次调用父类的Initialize方法,但是每次调用的含义不同,并不是父类初始化了三次。

    Load 和 Initialize 的区别

    1. 调用方式
      • Load是根据函数地址直接调用
      • Initialize是通过objc_msgSend调用
    2. 调用时刻
      • Loadruntime加载类,分类时候调用一次
      • Initialize是类第一次接收到消息时候调用,每个类只会调用1次(父类的Initialize方法可能被调用多次)
    3. 调用顺序
      • Load
        1. 先调用父类的Load,在调用子类的Load
        2. 再调用分类的Load,先编译的分类优先调用
      • Initialize
        1. 先初始化父类
        2. 在初始化子类(可能最终调用的还是父类的Initialize方法)

    关联对象

    常用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) 移除所有关联对象
    objc_AssociationPolicy 修饰符
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
    OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic
    OBJC_ASSOCIATION_RETAIN strong, atomic
    OBJC_ASSOCIATION_COPY copy, atomic
    • 在分类中使用的时候,object 一般为self
    • const void *Key 由于只是需要一个指针,为了节约内存,所以可以直接申明一个char类型的变量,不用赋值,也可以直接使用字符串, 这个值比较灵活
    • policy 根据需要存储的值类型来做确定
    • 如果是在Category中添加属性,get方法的Key可以使用隐式函数_cmd来表示当前方法地址, set方法中使用@selector(<get方法名>)确定Key
    • Category中使用关联对象,可以间接实现Category中使用成员变量的功能。
    • 如果想取消某个关联对象,直接把该对象的关联value设置为null就可以了。
    • void objc_removeAssociatedObjects(id object)会把这个object下面的所有关联对象都移除掉

    关联对象的原理

    关联对象的Key并不是存储在对应的类中的,由AssociationsManager统一管理并存储在AssociationsHashMap中。
    核心对象:

    • AssociationsManager
    • AssociationsHashMap
    • ObjectAssociationMap
    • ObjcAssociation
    associatedObject.png

    最后

    以上就是本篇的内容,势必会有一些遗漏和错误,欢迎斧正~

    相关文章

      网友评论

          本文标题:Category 学习

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