[toc]
类的加载
非懒加载类在运行时处理,懒加载编译期确定.
区分:方式为load
方法,把所有类的加载提前.
看代码,_getObjc2NonlazyClassList
是读取非懒加载类列表
只打印
LGTeache
r,但是LGPerson
未打印,在LGTeache
r实现了+(void)load
方法.
懒加载 类
编译期已经确定
创建
LGPerson
,不实现load方法
,调用alloc
创建一个实例
LGPerson *object = [LGPerson alloc];
因为方法的调用都会来到lookUpImpOrForward方法类,打下断点,发现走进如下的代码中,cls->isRealized()类没有初始化,所以为false,
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
代码跟进,发现走到
realizeClassMaybeSwiftMaybeRelock
这个方法
if 为oc使用,下面是swif才会走
realizeClassWithoutSwift
这个方法对类进行初始化,rw ro处理等等
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();
}
懒加载类 在第一次发送消息的进行处理.
分类的加载
懒加载类 + 懒加载分类
将
LGPerson
和LGPerson+test
中,load
方法全部注释掉,在read_images
里面,
发现打印结果没有
LGPerson
名字
LGPerson
调用alloc
方法,来到lookuporforward
发现,
这个流程和懒加载类一样
这个流程和上面的3流程相似,就是入口不是read_images,而是lookupImpOrForward方法,
lookupImpOrForward--->realizeClassMaybeSwiftAndLeaveLocked--->realizeClassMaybeSwiftMaybeRelock--->realizeClassWithoutSwift---->methodlizeClass。
然后class的data()中就已经存在懒加载的分类方法了。
懒加载类 + 非懒加载类
在
LGPerson+test
中,实现load
方法,在realizeClassWithoutSwift
打下断点,查看函数调用栈
查看流程,我们发现调用了
load_image
和prepare_load_methods
方法,虽然LGTeaher
类是懒加载,但是分类实现了load方法,所以从
dyld调用加载到内存. 而
load_image,我们在
dyld给
objc`的方法中有见过.
在
prepare_load_methods
方法中
首先,这个方法是将非懒加载类取出来,并且遍历将
load
方法加入到add_class_to_loadable_list
中,但是LGTeahe
r为懒加载类所以不会走
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
接下来调用
realizeClassWithoutSwift
将类的实现加到内存,紧接着,调用方法add_category_to_loadable_list()
将分类load加入数组中.
在懒加载类 + 非懒加载分类这种情况下,分类方法是在
load_images() -> prepare_load_methods() -> realizeClass() -> methodizeClass() -> attachCategories()
中将分类方法添加到类的rw.methods
中的。attachCategories()
的实现就不全贴出来了,其中有一行代码:
总结:
实现load 方法的时候
- 懒加载的类 + 非懒加载的分类 - 类似 - 子类实现了 - 父类 伴随这一期实现
- 发送消息的时候就去读取 -
realizeClassWithoutSwift
-methodlizeClass
- 就是我的类要在消息发送的时候才有 - 但是我的分类提前了 - 需要加载 -
read_images - addUnattachedCategoryForClass
- 但是没有实现类 就会在下面prepare_load_methods
实现 -
prepare_load_methods - realizeClassWithoutSwift
给你提前了实现类的信息 -unattachedCategoriesForClass
非懒加载类 + 懒加载分类
因为
class
为非懒加载,所以会直接会走正常的class
的加载初始化流程:read_images---->realizeClassWithoutSwift---->methodlizeClass
。
初始化完成后,class
的ro
中就已经存在懒加载的分类方法了。这里编译器已经自动的将category
方法加进去了;
在
read_image
里面,打下断点,发现分类的方法也已经在
read_images - realizeClassWithoutSwift - methodlizeClass
- 不需要添加表 - 直接在相应data() - ro
非懒加载类 + 非懒加载分类
主类和分类都实现load方法.接下来会发生什么?
在methodizeClass
打断点发现cats
为NULL
在
methodizeClass
在打印查看只有类的load
方法,并没有分类的load
方法
在methodizeClass,中有句代码
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
点进去查看
attachCategories
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
attachLists
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
完成对非懒加载分类的加载
因为
class
为非懒加载,所以首先会加载初始化class
;然后因为分类也是非懒加载的,所以会调用_read_images
中的分类相关的加载初始化,在此过程中调用了下面绑定分类到class
的方法:
addUnattachedCategoryForClass
这样在_read_images
过程中完成了分类的attach
。
图解
小技巧 注意点
未注册的类可以写入
ivar
但是如果2 和 3 调换位置 无法添加
在这一步已经返回 无法写入
注册之后ro
已经不能修改
未注册之前ro
还可以写入ivar
在ro
中
但是可以操作rw
添加属性 要实现setget方法 使用setvalue 底层调用set方法
动态类 创建添加 解耦合