美文网首页
3. 分类,类扩展

3. 分类,类扩展

作者: 如果大雨落下 | 来源:发表于2020-06-24 15:36 被阅读0次

    分类

    作用: 声明私有方法,分解体积大的类文件,把framework的私有方法公开
    Category源码:

    Category
    Category 是表示一个指向分类的结构体的指针,其定义如下:
    typedef struct objc_category *Category;
    struct objc_category {
      const char *name                          OBJC2_UNAVAILABLE; // 分类名
      Struct _class_t *cls;                             OBJC2_UNAVAILABLE; // 分类所属的类名
      struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 实例方法列表
      struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 类方法列表
      struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分类所实现的协议列表
      struct objc_properties_list *properties         OBJC2_UNAVAILABLE; // 属性列表
    
    }
    

    - 在编译的时候,会将每一个分类都转换成一个category_t的结构体,里面会存放原类的名称,实例方法列表,类方法列表,协议列表,还有属性列表

    - 在程序运行的时候,会将所有分类的方法,属性,协议等东西添加到原类里面

    - 添加顺序,是先将原类的存储数组通过memmove往后面移动,然后再将添加的分类信息copy过来,所以分类的信息会放在数组的前面,这也是为什么会优先调用分类方法的原因,如果分类里面有同样的方法,取决于分类的编译顺序,越后面编译的分类方法,越放在前面,越先被调用,这个里面的方法并不是被覆盖,其他分类方法也可以调用,只是执行顺序的问题

    3. 分类,类扩展
    • +load方法适合例外,是系统直接调用,其他方法是通过消息发送机制调用,所以会一级一级的查找,
    • Load的调用顺序都是优先原类,再调分类
    • Load方法的指针是单独记录的
    • 全局load方法的执行顺序,是按照编译顺序,先将每个原类的load方法加入数组,但是碰到了有父类的,先将父类的load加进去,分类的Load是直接按照编译顺序加进去,然后优先调原类的,再调分类的
    • 分类可以添加属性,因为分类的结构中有属性列表参数,但是分类的属性,不会自动生成成员变量,即_age这样的,而且只会声明get和set方法,但是不会生成方法的实现,如果要正常使用,就要自己增加方法的实现,通过runtime,用associ的方法来实现,里面的key,需要存一个地址值,只要保证唯一,可以用各种地址值,最优方案是用方法@selector的地址值,用get或set方法,那其中一个就可以用_cmd表示,_cmd就代表当前方法,每个方法有两个隐式参数,self和_cmd
    • 分类添加的属性并不会增加到原类当中,而是单独存储在一个地方,associetemanger,里面用hashmap来存储,相当于两层字典,第一层的key用的就是添加属性时传进去的object,也就是self,第二层的key里面存的就是添加时传进去的key,也就是一个唯一的地址值,存的值就是传进去的值,和值相关的policy(retain,copy等)


    注意:

    1. 分类是用于给原有类添加方法的,因为分类的结构体指针中,没有属性列表,只有方法列表。所以< 原则上讲它只能添加方法, 不能添加属性(成员变量),实际上可以通过其它方式添加属性> ,只能通过关联对象(objc_setAssociatedObject)来模拟实现成员变量,但其实质是关联内容,所有对象的关联内容都放在同一个全局容器哈希表中:AssociationsHashMap,由AssociationsManager统一管理。
    2. 分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);
    3. 可以在分类中访问原有类中.h中的属性;
    4. 如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;
    5. 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
    6. 一个类中用@property声明属性,编译器会自动帮我们生成成员变量和setter/getter,但分类的指针结构体中,根本没有属性列表。所以在分类中用@property声明属性,既无法生成成员变量也无法生成setter/getter。
      因此结论是:我们可以用@property声明属性,编译和运行都会通过,只要不使用程序也不会崩溃。但如果调用了_成员变量和setter/getter方法,报错就在所难免了。

    类扩展(Class Extension)

    Extension是Category的一个特例。类扩展与分类相比只少了分类的名称,所以称之为“匿名分类”。

    就是.m 文件中声名的

    @interface XXX ()
        //私有属性
        //私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)
    @end
    

    分类与类扩展的区别:

    • 分类中原则上只能增加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已);

    • 类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的(用范围只能在自身类,而不是子类或其他地方);

    • 类扩展中声明的方法没被实现,编译器会报警,但是分类中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而分类是在运行时添加到类中。

    • 类扩展不能像分类那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。

    • 定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。

    分类在运行时加入到原类中,类扩展在编译的时候就加入了原类

    相关文章

      网友评论

          本文标题:3. 分类,类扩展

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