上篇文章讲到,实现了+ load方法的类是非懒加载类,否则就是懒加载类。
- 非懒加载类:
+ load方法是在main函数之前被调用的。这个时候为了能后保证+ load方法能被调用,就必须提前把这个类加载好。
- 非懒加载类加载流程:
_dyld_objc_notify_register()->read_image->realizeClassWithoutSwift)->methodizeClass->attachLists对rw赋值。
- 懒加载:顾名思义,是平时不会被加载,只有在用到的时候才会被加载。
那么懒记载类是如何加载的呢?
一. 懒加载类的加载
在我们第一次使用这个类的时候,也就是给这个类发送第一条消息的时候,懒加载的类才会被真正加载。
在之前的篇章中我们也讲到过消息发送,消息发送中有一个很重要的方法lookUpImpOrForward。我们提到过
!cls->isRealized()用来初始化懒加载类的。在Object-C环境下,经过一系列的函数调用,会神奇的来到了我们上篇文章学习的realizeClassWithoutSwift。
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
...
}
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
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;
}
我们来测试下,懒加载类能不能满足!cls->isRealized()条件
1.1 测试一
AKPerson没有实现 + load 方法,是懒加载类,主程序调用[AKPerson alloc]的初始化方法。

结论一:懒加载类会在第一次调用的时候进行加载,加载的时机是在消息查找流程中的lookUpImpOrForward方法中。
1.2 测试二
父类AKPerson实现 + load 方法,子类AKStudnet不实现 + load 方法。清理缓存,主程序子类调用初始化方法。

结论二:父类实现
+ load, 子类不实现+ load。父类是非懒加载类,子类是懒加载类。
1.3 测试三
父类AKPerson不实现 + load 方法,子类AKStudnet实现 + load 方法。清理缓存,主程序子类先调用的初始化方法,父类再调用的初始化方法。
发现父类没有进入!cls->isRealized(), 父类是懒加载类。因为递归调用realizeClassWithoutSwift完善继承链并处理当前类的父类、元类;如果有父类,就通过addSubclass把当前类放到父类的子类列表中去
if (!cls) return nil;
...
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
...
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
...
// Connect this class to its superclass`s subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
结论三:如果子类实现
+ load,那么父类也会在子类被加载的时候,一起被加载。原因是子类在加载的时候会对父类和元类进行处理。
二. 分类的结构
2.1 clang
新建AKPerson + Test分类

clang -rewrite-objc AKPerson+Test.m -o category.cpp ,打开cpp文件可以发现。
- category存储在MachO文件的
__DATA的__的objc_catlist中
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_AKPerson_$_Test,
};
- AKPerson分类的结构如下
static struct _category_t _OBJC_$_CATEGORY_AKPerson_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"AKPerson",
0, // &OBJC_CLASS_$_AKPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_AKPerson_$_Test,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_AKPerson_$_Test,
0,
0,
};
2.2 分类的结构
objc源码中搜索category_t
struct category_t {
const char *name; // 类的名字,不是分类的名字
classref_t cls; // 类对象
struct method_list_t *instanceMethods; // 分类上存储的实例方法
struct method_list_t *classMethods; // 分类上存储的类方法
struct protocol_list_t *protocols; // 分类上所实现的协议
struct property_list_t *instanceProperties; // 分类所定义的实例属性
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties; // 分类所定义的类属性
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
为什么分类要将实例方法和类方法分开保存呢?
类和元类加载过程中不断编译,实例方法存在类中,类方法存在元类中,已经确定好其方法归属的地方;而分类晚于类和元类的加载。
三.分类的加载
我们现在知道了类分为了懒加载类 和 非懒加载类,它们的加载时机是不一样的,那么分类的加载又是怎么样的呢?
在分析前,还要搞清楚一点,分类必须依附于类而存在,如果只有分类,没有类,那么从逻辑上是说不通的,就算实现了,编译器也会忽略掉。
分类的加载在两处出现过:
_read_images的Discover categories.methodizeClass。
// Discover categories.
// 发现和处理所有Category
for (EACH_HEADER) {
// 外部循环遍历找到当前类,查找类对应的Category数组
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
// 内部循环遍历当前类的所有Category
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
// 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 将Category添加到对应Class的value中,value是Class对应的所有category数组
addUnattachedCategoryForClass(cat, cls, hi);
// 将Category的method、protocol、property添加到Class
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" : "");
}
}
// 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
// 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
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);
}
}
}
}
static void methodizeClass(Class cls)
{
...
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
...
}
为了方便定位,我们添加了一些调试代码

3.1 懒加载分类
懒加载类 & 懒加载分类

- 向懒加载类发送消息,
lookupOrForward->realizeClassWithoutSwift开始加载内存 methodizeClass处理父类、元类关系unattachedCategoriesForClass返回NULL- 另一处加载分类没有调用
非懒加载 & 懒加载分类

通过两次的断点调试,我们发现懒加载的分类,在运行时期间没有进行添加分类的操作,我们来看看分类中的方法是否被添加进来。
- 程序启动
dyld->_objc_init->map_images->_read_images->realizeClassWithoutSwift->methodizeClass加载类到内存中 methodizeClass处理父类、元类关系unattachedCategoriesForClas返回NULL- 另一处加载分类没有调用
懒加载的分类不是运行时添加的,我们来看看分类中的方法是否被添加进来。
- 查看一下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中的baseMethodList
(lldb) p $8.get(1)
(method_t) $15 = {
name = "load"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000c10 (objc-debug`+[AKPerson load] at AKPerson.m:12)
}
(lldb) p $8.get(2)
(method_t) $16 = {
name = "cate_instanceMethod"
types = 0x0000000100000f8c "v16@0:8"
imp = 0x0000000100000da0 (objc-debug`+[AKPerson(test) cate_instanceMethod] at AKPerson+test.m:34)
}
通过上述的lldb调试,我们发现,我们分类中的方法已经被添加到ro中了。
结论:不管是懒加载类或是非懒加载类,懒加载分类在编译时就确定了。
3.2 非懒加载分类
懒加载类 & 非懒加载分类
按照之前的理论,懒加载的类是在第一次发送消息的时候才会被加载的,函数调用栈应该是lookupImpOrForward -> realizeClassMaybeSwiftAndLeaveLocked -> realizeClassMaybeSwiftMaybeRelock -> realizeClassWithoutSwift -> methodizeClass。我们测试下。

这一次通过 unattachedCategoriesForClass 取到值了,并且在这之前 cls 的 ro 中并没有分类的 initialize 方法:
但是我们的函数调用栈,不是发送消息的流程,而走的是 load_images 的 prepare_load_methods 方法呢?
- 懒加载类要在消息发送的时候才会加载。
- 但是分类是非懒加载类,分类会提前走
read_images->addUnattachedCategoryForClass - 此时没有实现类 ,会在下面的
prepare_load_methods->realizeClassWithoutSwift->unattachedCategoriesForClass提前了实现类的信息
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 获取的所有的非懒加载分类
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
_getObjc2NonlazyCategoryList获取的所有的非懒加载分类,然后遍历这些非懒加载分类,加载这些分类所依赖的类。realizeClassWithoutSwift方法来加载类
结论:非懒加载分类让我们的懒加载类实现提前了,所以说懒加载类并不一定只会在第一次消息发送的时候加载,还要取决于有没有非懒加载的分类,如果有非懒加载的分类,那么就走的是
load_images里面的prepare_load_methods的realizeClassWithoutSwift。
非懒加载类 & 非懒加载分类
非懒加载类的流程我们十分熟悉了,在 _read_images 里面进行加载,而此时,分类也是非懒加载。
methodizeClass处断点:

unattachedCategoriesForClass 取出来的是 NULL,显然分类不是在这个地方被加载的
_read_images的Discover categories处断点

remethodizeClass 方法
/***********************************************************************
* remethodizeClass
* Attach outstanding categories to an existing class.
* Fixes up cls`s method list, protocol list, and property list.
* Updates method caches for cls and its subclasses.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertLocked();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
remethodizeClass有一个attachCategories方法
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
注意英文注释:
- Attach method lists and properties and protocols from categories to a class. -- 将分类的方法、属性和协议添加到类上
- Assumes the categories in cats are all loaded and sorted by load order, -- 分类按加载顺序加载完毕
- oldest categories first. 先加载的分类排在前面
attachCategories与attachLists原理基本一致 (参考类的加载):
- 调用
attachLists添加分类的方法、属性、协议 memmove将原数据移到末尾memcpy把新数据拷贝到起始位置
注:
- 其实
attachCategories这个方法只会在实现了非懒加载分类下才会被调用,而来到attachCategories之前又取决于类是否为懒加载, - 如果是懒加载,那么就在 load_images 里面去处理,
- 如果是非懒加载,那么就在 read_images 里面去处理。
四. 分类总结
- 类的加载
- 非懒加载类:
+ load方法是在main函数之前被调用的。这个时候为了能后保证+ load方法能被调用,就必须提前把这个类加载好。 - 懒加载:顾名思义,是平时不会被加载,只有在用到的时候才会被加载
- 分类的加载:
- 非懒加载分类:没有实现
load方法,编译时确定, 直接处理 data() - ro。 - 懒加载分离:实现了
load方法,运行时确定。
- 这也说明分类的加载和类的加载是不一样的,两者结合,我们有以下的结论:
| 情景 | 类的加载 | 分类的加载 |
|---|---|---|
| 懒加载分类 + 懒加载类 | 第一次发送 | 编译时 |
| 懒加载分类 + 非懒加载类 | _read_images | 编译时 |
| 非懒加载分类 + 懒加载类 | load_images(非懒加载分类让我们的懒加载类实现提前了) | 类加载之后的 methodizeClass |
| 非懒加载分类 + 非懒加载类 | _read_images | 类加载之后的 reMethodizeClass |
五. 类和分类的同名方法之争
如果类有多个分类,方法调用顺序如何呢?
Person类有AKPerson+Test1和AKPerson+Test1两个分类,三者都声明和实现类- sayHi方法,主程序调用[[AKPerson alloc] sayHi];。

- 分类都不实现
+ load方法

响应Compile Sources最后一个分类
- 分类都实现
+ load方法

Compile Sources最后一个分类
AKPerson+Test1实现+ load方法,AKPerson+Test2不实现+ load方法

AKPerson+Test2实现+ load方法,AKPerson+Test1不实现+ load方法

+ load 方法的分类。
结论1:
一般方法先调用分类,后调用主类。
- 分类的方法没有替换掉类已经有的方法, 分类的方法被放到了新方法列表的前面,而类的方法被放到了新方法列表的后面,这也就是我们平常所说的分类的方法会“覆盖”掉类的同名方法
- 因为运行时在查找方法时是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会返回imp。
结论2:
- 如果分类没实现
+load方法,就响应Compile Sources最后一个分类 - 如果都实现+load,响应·Compile Sources·最后一个分类
- 如果其中一个实现了
+load方法,响应非懒加载分类。因为懒加载分类在编译时就已经加载到内存,而非懒加载分类运行时才加载
六. load_images
懒加载类 + 非懒加载分类情况下,分类加载到内存时会调用load_image,那么我们在该种情况下进行探索.
在load_image实现处打下断点,发现类和分类都没有打印+load方法:load_image先于+load方法

注意英文注释:
- Discover load methods --
prepare_load_methods - Call +load methods (without runtimeLock - re-entrant) -- call_load_methods
6.1 prepare_load_methods 发现并准备+load方法
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 1.获取非懒加载类列表
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
// 2.获取非懒加载分类列表
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
const class_ro_t *ro = (const class_ro_t *)cls->data();
const char *cname = ro->name;
const char *oname = "AKPerson";
if (cname && (strcmp(cname, oname) == 0)) {
printf("_getObjc2NonlazyClassList 类名 :%s - %p 分类名: %s\n",cname,cls,cat->name);
}
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
/***********************************************************************
* add_category_to_loadable_list
* Category cat`s parent class exists and the category has been attached
* to its class. Schedule this category for +load after its parent class
* becomes connected and has its own +load method called.
**********************************************************************/
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don`t bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
prepare_load_methods 分析:
_getObjc2NonlazyClassList获取非懒加载类列表schedule_class_load遍历类列表
- 递归调用父类的
+ load方法,保证父类的+ load方法顺序排列在子类前面 add_class_to_loadable_list把类的+load方法存在loadable_classes里面
_getObjc2NonlazyCategoryList获取非懒加载分类列表- 遍历分类列表
realizeClassWithoutSwift来防止类没有初始化(若已经初始化了则不影响)add_category_to_loadable_list把分类的+load方法到loadable_categories
6.2 call_load_methods
现在我们知道+ load在 load_images里调用,到底怎么调用的呢?
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class`s +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren`t any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren`t any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
static void call_class_loads(void)
{
...
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, SEL_load);
}
...
}
static bool call_category_loads(void)
{
...
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
...
return new_categories_added;
}
- 通过
objc_autoreleasePoolPush压栈一个自动释放池 - do-while循环
- 循环调用
call_load_methods, 发送消息调用类的+load方法。 - 调用
call_category_loads,循环发送消息调用分类的+load方法。 (*load_method)(cls, SEL_load);调用+ load的过程,就是objc_msgSend(cls, SEL_load)的过程。
- 通过
objc_autoreleasePoolPop出栈一个自动释放池+load方法

七. initialize
7.1 initialize原理
Initializes the class before it receives its first message.
在这个类接收第一条消息之前调用。当该类不使用时,该方法可能永远不会被调用。
在lookUpImpOrForward -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass找到了它的踪迹。
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
...
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
...
callInitialize(cls);
...
}
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
- 如果有父类 且 父类没有
isInitialized,递归initializeNonMetaClass父类(推测:调用顺序 先父类后子类) callInitialize是一个普通的消息发送(推测:调用顺序 分类覆盖主类)
7.2 initialize 调用顺序测试
- 是不是先父类后子类
AKPerson父类和AKTeacher子类都实现initialize方法
- 主程序 先调用
父类,后调用子类

- 主程序 先调用
子类, 后调用父类

- 是不是分类覆盖主类
AKPerson + Test分类和 AKTeacher + Test分类都实现initialize方法,主程序先后调用子类和父类初始化方法。

- 是不是只调用一次
AKPerson父类实现initialize方法,AKTeacher子类不实现initialize方法,主程序调用子类初始化方法。

7.3 initialize 总结
initialize走普通的消息发送机制。所以分类覆盖主类,当有多个分类都实现了initialize方法,执行最后被加载到内存中的分类的方法。initialize在类或者其子类的第一个方法被调用前(发送消息前)调用- 如果父类和子类都实现了
initialize方法,在调用子类时,
- 如果
父类的initialize方法调用过,则只调用子类的initialize方法; - 如果
父类的initialize没用过,则先调用父类的initialize方法,在调用子类的initialize方法。(此时,再初始化父类的时候,不会再调用initialize方法)
- 父类实现,子类不实现,调用子类时,会调用两次
父类的initialize方法
八. 总结
本篇主要学习了 懒加载类 非懒加载类 懒加载分类 非懒加载分类的加载; + load 和 + initialize 的调用。也是面试中百分比会被问到的地方,希望有所帮助。