iOS分类的实现原理简记


该文为分类原理的简单记录,总结自如下文章,感谢作者分享:

  • iOS底层原理总结
  • iOS分类底层实现原理小记
  • 深入理解Objective-C:Category

1、分类的结构

分类的结构体如下
(源码详见:objc-runtime-new.h )

struct category_t {
    const char *name; //类名
    classref_t cls; //分类所属的类

    //category中所有给类添加的实例方法的列表(instanceMethods)
    WrappedPtr instanceMethods;

    //category中所有添加的类方法的列表(classMethods)
    WrappedPtr classMethods;

    //category实现的所有协议的列表(protocols)
    struct protocol_list_t *protocols;

    //category中添加的所有属性(instanceProperties)
    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;
    }
};

2、分类的编译过程

通过如下命令将分类的m文件进行转换,分析其编译过程

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxx+xxx.m

structcategory_t{

constchar *name;// 类名

classref_tcls; // 分类所属的类

// 实例方法列表
structmethod_list_t*instanceMethods;

// 类方法列表
structmethod_list_t*classMethods;

// 遵循的协议列表
structprotocol_list_t*protocols;

// 属性列表
structproperty_list_t*instanceProperties;

// 如果是元类,就返回类方法列表;否则返回实例方法列表
method_list_t*methodsForMeta(boolisMeta) {

if(isMeta) {

  return classMethods; 

 }else{

  return instanceMethods;

 }

 }

// 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,关于分类的实例属性,下文阐述
property_list_t*propertiesForMeta(boolisMeta) {

if(isMeta) {

  return nil;// 元类没有属性;

}else{

  return instanceProperties;//实例属性

 } 

 }

};

3、分类的实现原理:

由上可得,分类在编译过程中,会生成 类方法列表实例方法列表属性列表 等,但是却 没有 实例变量列表(_ivar_list_t) ,可对比分类所属类的编译结果看,分类所属类是存在实例变量列表的。然后,再来对比 实例方法列表 ,还能发现分类的 实例方法列表 中,并未对分类属性生成 getter/setter 方法。

所以,这就是为什么 分类不能添加属性 的原因。

4、分类的加载

  • 分类是在 运行时 进行加载的,OC运行时,入口方法为 _objc_init 其加载调用栈如下:
_objc_init   //runtime的初始化函数,进行初始化操作,注册了镜像状态改变时的回调函数

└── map_2_images //加锁并调用 map_images_nolock

    └── map_images_nolock //完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了 _read_images

        └── _read_images //加载类、Protocol、Category,加载分类的代码就写在 _read_images 函数的尾部

_objc_init 函数在 objc-os.mm 中,_read_images 方法在objc-runtime-new.mm 中。

  • 加载过程:

1、把分类的 实例方法属性协议 添加到类的实例对象中原本存储的 实例方法属性协议列表前面
2、把分类的 类方法协议添加到类的元类上。

如此,保证了分类方法 优先调用,注意,不是覆盖,而是共同存在在实例方法列表中,只是分类在前而已。

你可能感兴趣的:(iOS分类的实现原理简记)