问题
- Category 的底层结构
- 分类的加载处理过程
- 分类中load方法加载过程
- initialize方法的调用过程,准遵循原理
- initialize 方法 和 load 方法区别
答案
- Category 的底层结构
# 通过
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc MJPerson+Test1.m -o MJPerson+Test1_arm64.cpp
得到了 category_t 分类的底层结构
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
# 通过 objc 源码 objc-runtime-new.h 文件中分类结构体的定义
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;
// 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;
}
};
-
分类的加载处理过程
分类函数加载到内存当中如下图:
分类函数加载图.png
- 在程序编译的时候,每一个分类(Category )都是一个Category_t 对象里面存储着所有的方法,协议,等
- Category 中的方法,属性,协议数据 是在程序启动的时候通过runtime加载到内存道中去的,并合并到一个大数组中
- 后编译的Category数据,会在数组的最前面
- 当使用消息机制访问方法的时候,也就是后面编译的先访问,没有在找后面的如上图显示的访问顺序
- 将合并后的分类数据(方法,协议,属性),插入到类原来数据的前面里面还涉及到两个方法 memmove,memcopy.
- 分类中load方法加载过程
- +load方法会在runtime加载类和分类的时候调用(也就是程序启动的时候就会调用,在Main函数的前面)
- 每个类,分类的 +load 方法,在程序运行过程中,只会主动的调用一次
- 调用顺序:
- 先调动类的+load方法
- 按照编译先后顺序调用(先编译,先调用)
- 调用子类的+load之前会先调用父类的 +load 方法
- 再调用分类的 +load方法
- 先调动类的+load方法
- initialize方法的调用过程,准遵循原理
- 调用顺序
- 先调用父类的+initialize,在调用子类的 +initialize方法
- 先初始化父类,再初始化子类,每一个类只会初始化一次
- initialize 遵循消息机制原理
- initialize 方法 和 load 方法区别:
-
initialize:
- 调用顺序是:先调用父类,在调用子类
- 在类第一次接收到消息的时候调用
- initialize 方法只调用一次,而且是被动调用
-
load:
- 调用顺序是:先调用父类的load方法,在调用子类的laod方法,在调用分类的load方法
- load 实在运行时加载类的时候调用的,也就是项目启动的时候调用的,调用时通过找到load 函数地址直接调 用的,而不是通过消息机制调用的.
网友评论