Category与+load、+initialize方法
Catergory
在编译时,会将分类生成一个category_t结构体
category_t结构体分类的本质:runtime会将分类中的方法、协议、属性分别拼接成3个数组,且分类方法、协议、属性的顺序和分类的编译顺序有关,后编译的会在前面。然后回将生成的数组进行momerymove(内存移动)操作,拼接在类对象和元类对象的rw->method_list等属性的最前面
分类源码调用顺序:
在pobjc-os.mm文件中
_objc_init
map_images
map_images_nolock
在pobjc-runtime-new.mm 文件中
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、 memcpy
+load方法
调用时机:会在runtime加载类和分类时调用,且只调用一次。
调用顺序:分类的+ load方法和类的 + load方法都会调用,且一定是原类中的load方法先调用,因为在源码中,会先找到原类中的+ load方法的内存地址,直接单独调用,再按照分类文件的编译顺序进行分别调用分类中的+ load方法,先编译的先调用。
源码调用顺序:
+load方法调用过程+initialize方法
调用时机:+initialize方法,是在类第一次调用objc_msgSend方法前调用,即类第一次接收到消息前调用
调用顺序:+initialize方法在源码中,先判断当前类是否调用过+initialize(initialized);如果没有调用过,则会通过递归的方法寻找其父类的+initialize方法;接下来会判断当前父类是否initialized,如果父类没有调用过则父类先调用callInitialize即调用+initialize方法,再进行调用子类的+initialize方法。
源码调用顺序:
在pobjc-msg-arm64.s文件中
objc_msgSend
在pobjc-runtime-new.mm文件中
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)
区别:
1)+initialize方法是通过objc_msgSend方法调用,与load方法的直接通过地址指针进行调用不同
2)如果子类没有调用+initialize方法,且子类不存在+initialize方法,则会调用父类的+initialize方法,因此父类的+initialize方法有可能会调用多次
3)因为+initialize方法是通过objc_msgSend方法调用,因此分类的+initialize方法会覆盖本身的+initialize方法