每一个类,每一个分类中都存在一个
+(void)load
方法。咱们不须要显式的进行调用,当runtime动态加载类、分类的时候会进行调用。数组
建立一个command line 项目 建立几个类。Student继承自Person,每一个类包括其Category中都实现load方法,运行咱们发现控制台打印了如下信息。函数
咱们发现类中的load方法没有被Category中的load方法覆盖,而是全都进行了调用。这是为何呢?另外load方法的调用顺序有什么规律呢?指针
经过runtime源码咱们能够看到在runtime的入口函数void _objc_init(void)
(存在于objc_os.mm中)中一段代码加载images(镜像)。_dyld_objc_notify_register(&map_images, load_images, unmap_image);
日志
进入load_images能够发现有调用load方法的函数code
经过函数名能够知道这里实现对load方法展开调用。查看该方法的实现,咱们发现它又是经过call_class_loads对类的load方法进行调用,经过call_category_loads对category中的load方法进行调用的。它使用了一个while循环首先调用类的load方法,类的load方法所有调用完成以后,再调用Category的load方法。cdn
查看call_class_load函数的实现,咱们发现它经过遍历一个数组获取一个结构体loadable_class。blog
这里的method就是load函数的地址,而call_class_loads获取函数地址,直接进行调用。call_category_loads也是如此。继承
那么loadable_classes这个list是按照什么规律生成的呢?递归
从新观察load_iamges函数,咱们发现有一个prepare_load_methods函数ssl
这个方法遍历classlist,经过schedule_class_load
函数准备class的load方法。这个函数中有递归调用了本身,首先处理父类,最后调用了add_class_to_loadable_list
函数,将load生成loadable_classes
数组和loadable_classes_used
可调用的load方法的数量。
经过这个函数的实现,咱们也能够验证上文中所说的loadable_class
中的method就是load方法,咱们能够看到method是经过调用getLoadMethod
获取并添加到结构体loadable_class
中的。
Category的将load方法加载到数组的方法跟class基本一致,可是Category是按照编译顺序进行添加的。
综上所述:
+(void)load
方法是在runtime加载类或分类的时候调用的。loadable_classes_used
数量会置为0。initialize
在一个类刚刚接收到消息的时候进行调用。利用上面的代码,实现initialize
方法,在main函数中让student给alloc发消息。获得如下打印:
通过上面的经验能够得知,initialize是经过objc_msgSend方法调用的,因此只打印了category的initialize中的日志。而category的调用,根据Category的底层实现能够得知,后编译的被调用了。
因为objc_msgSend的实现没有开源,因此经过查看objc-runtime-new.mm中的获取实例方法的函数(class_getInstanceMethod
)进行窥探。
在该方法中经过调用lookUpImpOrNil
方法查找方法的实现,这个方法又调用了lookUpImpOrForward
方法继续查找。
在lookUpImpOrForward
方法中若是存在initialize
而且没有调用过,则经过_class_initialize (_class_getNonMetaClass(cls, inst));
调用initialize。
_class_initialize (_class_getNonMetaClass(cls, inst));
中经过递归先经过callInitialize(cls);
调用父类的initialize。
callInitialize(cls);
中就能够看出是经过objc_msgSend
调用的。
因此会执行经过isa指针查找方法实现的过程。
+(void)initialize
是经过objc_msgSend调用的+(void)initialize
,因此父类的+(void)initialize
方法会被调用屡次。