在日常开发中我们想要为一个类(系统类或者自定义类)添加方法的时候,最常用的手段就是写一个分类,那么我们创建分类是何时被加载进类中的呢?
通过objc
的源码可知我们在从Mach-O
中读取分类时分为两中。
GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist");
GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
就是懒加载分类和非懒加载分类
1、懒加载分类
懒加载分类,也就是没有实现+(void)load
方法的分类。
非懒加载类-懒加载分类
通过两次的断点调试,我们发现懒加载的分类,在运行时期间没有进行添加分类的操作,我们来看看分类中的方法是否被添加进来。
1.1、lldb调试分类是否被添加
- 获取类结构
(lldb) x/4gx cls
0x1000012c0: 0x001d800100b390f1 0x0000000100b390f0
0x1000012d0: 0x00000001003dc230 0x0000000000000000
- 通过地址偏移拿到
bits
(lldb) p (class_data_bits_t *)0x1000012f0
(class_data_bits_t *) $1 = 0x00000001000012f0
- 获取
bits
中的class_rw_t
(lldb) p $1->data()
(class_rw_t *) $3 = 0x0000000100b39140
- 查看一下
class_rw_t
中的内容
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148139008
version = 7
ro = 0x00000001000011f0
methods = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100001168
arrayAndFlag = 4294971752
}
}
}
properties = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
protocols = {
list_array_tt<unsigned long, protocol_list_t> = {
= {
list = 0x0000000000000000
arrayAndFlag = 0
}
}
}
firstSubclass = nil
nextSiblingClass = 0x00007fff92d22080
demangledName = 0x0000000000000000
}
- 接下来我们再看一下
ro
中的内容
(lldb) p $4.ro
(const class_ro_t *) $5 = 0x00000001000011f0
(lldb) p *$5
(const class_ro_t) $6 = {
flags = 389
instanceStart = 40
instanceSize = 40
reserved = 0
ivarLayout = 0x0000000000000000
name = 0x0000000100000f7b "LGTeacher"
baseMethodList = 0x0000000100001168
baseProtocols = 0x0000000000000000
ivars = 0x0000000000000000
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000000000000
_swiftMetadataInitializer_NEVER_USE = {}
}
- 继续查看
ro
中的baseMethodList
(lldb) p $6.baseMethodList
(method_list_t *const) $7 = 0x0000000100001168
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 3
first = {
name = "sayMaster"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000c40 (objc-debug`+[LGTeacher sayMaster] at LGTeacher.m:16)
}
}
}
(lldb) p $8.get(0)
(method_t) $14 = {
name = "sayMaster"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000c40 (objc-debug`+[LGTeacher sayMaster] at LGTeacher.m:16)
}
(lldb) p $8.get(1)
(method_t) $15 = {
name = "load"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000c10 (objc-debug`+[LGTeacher load] at LGTeacher.m:12)
}
(lldb) p $8.get(2)
(method_t) $16 = {
name = "cate_classMethod2"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000da0 (objc-debug`+[LGTeacher(test) cate_classMethod2] at LGTeacher+test.m:30)
}
通过上述的lldb
调试,我们发现,我们分类中的方法已经被添加到ro
中了。
所以我们得出懒加载的分类- 编译处理 - 直接处理 data() - ro。
2、非懒加载分类
2.1、非懒加载类-非懒加载分类
非懒加载类-非懒加载分类- read_images - realizeClassWithoutSwift - methodlizeClass - addUnattachedCategoryForClass(这里还没有)
- 继续read_images流程
// Discover categories.
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
//省略。。。。
//下面的代码实现了实例分别对实例方法和类方法做了区分
bool classExists = NO;
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" : "");
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
上述代码分别对实例方法和类方法进行了区分,原因是实例方法存储在类对象中,而类方法是存储在元类中,所以要分别进行处理。
if (cls->isRealized()) {
remethodizeClass(cls); -> 实现类信息
}
最后通过attachCategories
加载分类数据进来
2.2、懒加载类-非懒加载分类
懒加载类-非懒加载分类通过调用堆栈我们可以清楚的看到在
load_image
阶段因为需要调用+(void)load
方法所以类的加载被提前了,也就是在prepare_load_methods
函数中执行了类的加载,然后继续在将分类中的方法加载到类中。
至此,我们关于分类的加载就分析完毕了。
网友评论