美文网首页ios 开发
iOS基础知识(四)分类(category)和扩展(extens

iOS基础知识(四)分类(category)和扩展(extens

作者: iOS小洁 | 来源:发表于2020-12-24 23:31 被阅读0次

    首先,分别来说下分类(category)和扩展(extension)的用处。然后来解读一下分类的底层实现

    分类常见的应用
    1、可以将臃肿的类根据不同业务划分为多个模块,方便进行管理
    2、对原有的类进行扩展,给类添加方法。特别是给系统自带的类方法

    分类的注意点
    1、分类里面可以定义@property属性,但是不会自动生成set和get方法,以及对应的成员变量
    2、分类不能添加成员变量,但是可以通过关联对象的方式添加成员变量
    3、分类中有和原有类同名的方法, 会优先调用分类中的方法
    4、多个分类中同名方法,优先调用后面参与编译的分类里面的方法

    扩展的应用
    1、为一个类声明一些额外的方法和属性
    2、常用在.m文件中添加一些私有属性。创建viewcontroller文件时会自动在.m文件内最上面实现扩展代码

    扩展注意点
    1、扩展只能声明方法、属性,具体实现需要在源文件中实现

    分类的底层结构

    struct category_t {
        const char *name;
        classref_t cls;
        WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
        WrappedPtr<method_list_t, PtrauthStrip> classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
        
        protocol_list_t *protocolsForMeta(bool isMeta) {
            if (isMeta) return nullptr;
            else return protocols;
        }
    };
    

    分类的加载过程
    1、通过Runtime加载某个类的所有Category数据
    2、把所有Category的方法、属性、协议数据,合并到一个大数组中。后面参与编译的Category数据,会在数组的前面
    3、将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面

    源码调用顺序
    1、_objc_init
    2、map_images
    3、map_images_nolock
    4、_read_images
    5、remethodizeClass
    6、attachCategories
    7、attachLists
    8、realloc、memmove、 memcpy

    通过分析就知道为什么分类不能添加成员变量,分类同名方法调用顺序

    扩展
    网上分类文章很多,但是关于扩展的文章较少,而且还都有错误,扩展的底层源码没法分析,下面就来验证一下网上关于扩展的说法

    1、不能给系统类添加扩展,关于这个说法尝试一下几步
    创建一个iOS工程,给NSObject添加一个扩展NSObject+Extension,编译没问题,运行没问题,所以可以添加扩展文件
    在扩展文件中添加属性objName,编译没问题,运行没问题
    在控制器中导入扩展的头文件,创建NSObject对象obj,给对象obj.objName赋值,编译没问题,运行报错unrecognized selector sent to instance 0x6000037b4630。找不到对应的方法
    创建NSObject分类NSObject+Category,在分类.m文件中导入NSObject+Extension,在分类中使用属性objName。编译没问题,运行报错
    在NSObject+Extension中添加方法声明,在分类中实现方法,在控制器中调用方法,编译没问题,运行没问题

    总结如下,可以给系统类添加Extension,但是扩展中添加的方法和属性没有意义。
    可以再扩展中添加方法声明,在分类中添加具体的方法实现。
    在系统类扩展中声明的属性,不会自动生成set方法和get方法。可以再扩展中声明,在分类中添加关联对象。
    由此可见,扩展就相当于对类.h的扩展,由于系统类.m文件拿不到,所以对系统类的扩展没有意义,还需要结合分类使用,反而要多导入一个头文件

    2、类扩展中声明的方法没被实现,编译器会报警,但是分类中的方法没被实现编译器是不会有任何警告的
    这个说法还是不对的
    尝试了一下,结果是
    扩展中声明的方法没有实现,会在原来的类里面警告
    分类中明的方法没有实现,会在分类里面警告

    3、非系统类扩展,只有把扩展头文件导入到原类的.m文件中会自动生成set和get方法。扩展声明的方法需要在分类或者元类的.m文件内实现

    通过对扩展的分析,又拓展了一个扩展的应用场景,三方库对系统类添加的分类中如果有私有的方法,正常情况是没办法调用的,但是我们可以给这个系统类添加一个拓展,将私有方法在扩展中声明一下就可以调用了

    相关文章

      网友评论

        本文标题:iOS基础知识(四)分类(category)和扩展(extens

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