类的懒加载流程
在我们的上篇文章中提到:_read_images
方法中获取到classlist,然后循环使用realizeClassWithoutSwift
对class进行了初始化处理。我们就在这个循环中打印所有的class,然后有个神奇的发现:如果我们自定义的类中没有实现+(void)load
方法,那么上面的classlist中就不会存在这个类。只要实现了load
方法的class才会存在。为什么会这样呢?
原来我们的系统对加载class进行了懒加载的优化,也就是如果没有用到某个class的话,就不加载,只有使用到的时候才会加载。因为load
方法在编译的时候调用,所以也必须在此之前初始化这个class。所以如果实现了load
方法的话,就会取消该class的懒加载优化,直接进行加载。
也就是说如果class实现了load
方法,那么就以非懒加载方式加载,否则以懒加载方式加载。
在Images加载一中研究的都是class的非懒加载,下面我们来研究下class懒加载的流程。
LGPerson *object = [LGPerson alloc];
我们再调用以上的代码,就会给LGPerson类对象发送消息。发送消息就会走我们熟悉的lookupImpOrForward
方法。我们进入该方法,发现一下判断代码
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
如果cls没有实现的话,就会走realizeClassMaybeSwiftAndLeaveLocked
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
然后调用realizeClassMaybeSwiftMaybeRelock
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
// Non-Swift class. Realize it now with the lock still held.
// fixme wrong in the future for objc subclasses of swift classes
realizeClassWithoutSwift(cls);
if (!leaveLocked) lock.unlock();
} else {
// Swift class. We need to drop locks and call the Swift
// runtime to initialize it.
lock.unlock();
cls = realizeSwiftClass(cls);
assert(cls->isRealized()); // callback must have provoked realization
if (leaveLocked) lock.lock();
}
return cls;
}
如果是OC方法的话,就会调用realizeClassWithoutSwift
这个方法。这个方法不就是我们在研究Images加载一中,以非懒加载方式加载class时在_read_images
调用的方法吗。
realizeClassWithoutSwift
中完成了对class的ro、rw的赋值,以及递归父类和元类的初始化赋值。到此为止class的懒加载流程和非懒加载流程都调用了realizeClassWithoutSwift
方法。
也就是说懒加载class是在第一次接受消息的时候进行加载的。
下面我们来看下分类的加载流程
首先我们添加几个分类方法,然后通过clang来看下分类的底层实现。通过clang命令生成对应的c++文件,c++文件内容比较多,我们可以直接拉到文件的底部查看我们的oc代码对应的内容。
很明显可以找到_category_t
这个对象。然后我们可以搜索到_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的源码中搜索category_t
找到在c语言中对应的定义。
结构体中包含了名字、对应的class以及协议、属性、实例方法列表和类方法列表。为什么这里的实例方法和类方法分成了两个列表呢?因为实例方法要attach到类的方法列表中,而类方法要attach到元类的方法列表中。
我们知道了类的加载分为懒加载和非懒加载,同样分类的加载也分为懒加载和非懒加载。这样就是有四个组合:
- 懒加载类+非懒加载分类;
- 非懒加载类+非懒加载分类;
- 非懒加载类+懒加载分类;
- 懒加载类+懒加载分类;
好了,下面我们来一一的研究。
1、懒加载类+非懒加载分类;
因为分类为非懒加载,所以肯定会调用_read_images
方法,然后走到该方法的处理分类的地方:
category_t **catlist = _getObjc2CategoryList(hi, &count);
然后一个for循环处理catlist:
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
// ......省略一些无用的代码
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
// ......省略一些无用的代码
}
首先获取到list中的一个cat,然后调用了addUnattachedCategoryForClass
方法,将category暂时存起来,此时class还没有初始化。
还记得_objc_init
中的这个方法吗
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
其中的load_images
函数调用了prepare_load_methods
函数,而在prepare_load_methods
函数中获取到非懒加载的categoryList,如果存在非懒加载categoryList的话,就会在此时调用realizeClassWithoutSwift(cls);
方法来初始化该class,然后开始调用methodizeClass
方法,在methodizeClass
方法中就会开始绑定categoryList到对应的class
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
这样本来以来,本来懒加载类是会在运行时接受消息的时候才会出初始化的,结果因为非懒加载的分类而被提前初始化了。
2、非懒加载类+非懒加载分类;
因为class为非懒加载,所以首先会加载初始化class;然后因为分类也是非懒加载的,所以会调用_read_images
中的分类相关的加载初始化,在此过程中调用了下面绑定分类到class的方法:
addUnattachedCategoryForClass
这样在_read_images
过程中完成了分类的attach。
3、非懒加载类+懒加载分类;
因为class为非懒加载,所以会直接会走正常的class的加载初始化流程:read_images
---->realizeClassWithoutSwift
---->methodlizeClass
。
初始化完成后,class的ro中就已经存在懒加载的分类方法了。这里编译器已经自动的将category方法加进去了;
4、懒加载类+懒加载分类;
这个流程和上面的3流程相似,就是入口不是read_images
,而是lookupImpOrForward
方法,
lookupImpOrForward
--->realizeClassMaybeSwiftAndLeaveLocked
--->realizeClassMaybeSwiftMaybeRelock
--->realizeClassWithoutSwift
---->methodlizeClass
。
然后class的data()中就已经存在懒加载的分类方法了。