ObjC load与initialize 简析

+ (void)load

每一个类,每一个分类中都存在一个+(void)load方法。咱们不须要显式的进行调用,当runtime动态加载类、分类的时候会进行调用。数组

实例文件目录

建立一个command line 项目 建立几个类。Student继承自Person,每一个类包括其Category中都实现load方法,运行咱们发现控制台打印了如下信息。函数

load信息

咱们发现类中的load方法没有被Category中的load方法覆盖,而是全都进行了调用。这是为何呢?另外load方法的调用顺序有什么规律呢?指针

runtime 源码-窥探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_images

经过函数名能够知道这里实现对load方法展开调用。查看该方法的实现,咱们发现它又是经过call_class_loads对类的load方法进行调用,经过call_category_loads对category中的load方法进行调用的。它使用了一个while循环首先调用类的load方法,类的load方法所有调用完成以后,再调用Category的load方法。cdn

查看call_class_load函数的实现,咱们发现它经过遍历一个数组获取一个结构体loadable_class。blog

loadable_class

这里的method就是load函数的地址,而call_class_loads获取函数地址,直接进行调用。call_category_loads也是如此。继承

那么loadable_classes这个list是按照什么规律生成的呢?递归

从新观察load_iamges函数,咱们发现有一个prepare_load_methods函数ssl

prepare_load_methods

这个方法遍历classlist,经过schedule_class_load函数准备class的load方法。这个函数中有递归调用了本身,首先处理父类,最后调用了add_class_to_loadable_list函数,将load生成loadable_classes数组和loadable_classes_used可调用的load方法的数量。

add_class_to_loadable_list

经过这个函数的实现,咱们也能够验证上文中所说的loadable_class中的method就是load方法,咱们能够看到method是经过调用getLoadMethod获取并添加到结构体loadable_class中的。

Category的将load方法加载到数组的方法跟class基本一致,可是Category是按照编译顺序进行添加的。

+ (void)load的一些问题

综上所述:

  • +(void)load方法是在runtime加载类或分类的时候调用的。
  • 每一个类分类的load在程序运行过程当中只会调用一次。调用的完成loadable_classes_used数量会置为0。
  • 先调用类的load方法(先编译的先调用)父类load优先调用
  • 分类的load方法 先编译的先调用
  • load方法是直接查找到函数地址进行调用的而不是经过消息发送机制调用的,因此不会出现方法覆盖

+(void)initialize

initialize在一个类刚刚接收到消息的时候进行调用。利用上面的代码,实现initialize方法,在main函数中让student给alloc发消息。获得如下打印:

initialize信息

通过上面的经验能够得知,initialize是经过objc_msgSend方法调用的,因此只打印了category的initialize中的日志。而category的调用,根据Category的底层实现能够得知,后编译的被调用了。

runtime 源码-窥探initialize方法的调用

因为objc_msgSend的实现没有开源,因此经过查看objc-runtime-new.mm中的获取实例方法的函数(class_getInstanceMethod)进行窥探。

在该方法中经过调用lookUpImpOrNil方法查找方法的实现,这个方法又调用了lookUpImpOrForward方法继续查找。

lookUpImpOrForward方法中若是存在initialize而且没有调用过,则经过_class_initialize (_class_getNonMetaClass(cls, inst));调用initialize。

_class_initialize

_class_initialize (_class_getNonMetaClass(cls, inst));中经过递归先经过callInitialize(cls);调用父类的initialize。

callInitialize(cls);中就能够看出是经过objc_msgSend调用的。

调用objc_msgSend

因此会执行经过isa指针查找方法实现的过程。

+(void)initialize的一些问题

  • +(void)initialize是经过objc_msgSend调用的
  • category实现了initialize方法会覆盖父类的initilize方法
  • initialize也只初始化一次,可是因为是经过objc_msgSend的调用的,若是子类没有实现initialize,而父类实现了,则子类每次初始化的时候都会调用父类的+(void)initialize,因此父类的+(void)initialize方法会被调用屡次。
相关文章
相关标签/搜索