本文是Objective-C系列的第11篇,主要讲述了及load
和initialize
两个特殊方法的相关特性及其底层的实现。git
Objective-C(七)对象内存分析github
Objective-C(十二)关联对象fetch
在讲述以前,咱们先把该两个方法经常使用到的一些知识点先列出。ui
根据下列顺序,阅读objc源码便可。spa
测试用例源码在**02-load-initialize**ssr
+load
+load
方法+load
方法@implementation BFPerson
+ (void)load
{
NSLog(@"+[BFPerson load]");
}
@end
@implementation BFBoy
+ (void)load
{
NSLog(@"+[BFBoy load]");
}
@end
复制代码
输出日志:
14:21:44.542004+0800 Category[4483:5050573] +[BFPerson load]
14:21:44.542545+0800 Category[4483:5050573] +[BFBoy load]
load
方法+load
方法+load
方法
+load
方法调用顺序与编译顺序一致@implementation BFBoy
+ (void)load
{ NSLog(@"+[BFBoy load]");}
@end
@implementation BFBoy (Handsome)
+ (void)load
{ NSLog(@"+[BFBoy load]--Handsome Cat");}
@end
@implementation BFBoy (Tall)
+ (void)load
{ NSLog(@"+[BFBoy load]--Tall Cat"); }
@end
复制代码
输出日志:
14:24:09.679907+0800 Category[4565:5054709] +[BFPerson load]
14:24:09.680555+0800 Category[4565:5054709] +[BFBoy load]
14:24:09.680632+0800 Category[4565:5054709] +[BFPerson load]--Wrok Cat
14:24:09.680749+0800 Category[4565:5054709] +[BFBoy load]--Tall Cat
14:24:09.680845+0800 Category[4565:5054709] +[BFBoy load]--Handsome Cat
void prepare_load_methods(const headerType *mhdr)
{
//1.获取非懒加载的类列表
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//1. 先将类及其父类的load ---> loadable_classes
schedule_class_load(remapClass(classlist[i]));
}
//2.获取分类列表
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
// 2. 将分类的load ---> loadable_categories
add_category_to_loadable_list(cat);
}
}
复制代码
调用load方法,即将上一步抽取出来的方法列表loadable_classes
、loadable_categories
,逐一调用便可。
+load
方法
+load
方法+load
方法+load
方法void call_load_methods(void)
{
do {
// 1. 会先迭代调用完全部的类的load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 依次调用全部分类的load方法
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
}
复制代码
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod(); //获取类中load的IMP
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
/* 1. loadable_class :Class load方法的结构体 1.1 class与load一一对应 1.2 struct loadable_class { Class cls; // may be nil IMP method; }; 2. loadable_classes 存放loadable_class列表 3. loadable_classes_used 当前load存放在loadable_class序号 4. loadable_classes 每次分配的内存为 loadable_classes_allocated*2 + 16; 4.1 realloc void *realloc(void *ptr, size_t size) 从新调整以前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。 */
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
复制代码
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));
}
/* 1. loadable_category :存放Category load方法的结构体 1.1 class与load一一对应 1.2 struct loadable_category { Category cat; // may be nil IMP method; }; 2. loadable_categories 存放loadable_category列表 3. loadable_categories_used 当前load存放在loadable_category序号 4. loadable_categories 分配的内存 4.1 realloc void *realloc(void *ptr, size_t size) 从新调整以前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。 4.2 每次分配为 loadable_categories_allocated*2 + 16; 即上次分配后,一直能使用到(上次的2倍+16)次,才会再次分配 */
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++;
}
复制代码
(*load_method)(cls, SEL_load);
复制代码
因为+initialize
是在类第一次接收到消息是调用,咱们调用**[BFPerson alloc];**时,就会调用。
咱们根据汇编debug获得了下面流程:
> 1. objc_msgSend
> 2. objc_msgSend_uncached
> 3._class_lookupMethodAndLoadCache3
> 4. lookUpImpOrForward
> 5. _class_initialize
5.1>> _setThisThreadIsInitializingClass(objc_class*)
5.1.1>>> _fetchInitializingClassList
5.2>> CALLING_SOME+initialize_METHOD:
5.2.1>>> objc_msgSend(cls, SEL_initialize)
//这一步会打印 +[BFPerson initialize]--Study Cat
5.3>> lockAndFinishInitializing
复制代码
而后,咱们去objc4源码挖矿。
咱们发现,1
、2
在源码中都是汇编,3
是可读的C代码。就从3
开始。
测试用例源码在**02-load-initialize**
initialize
@implementation BFPerson
+ (void)initialize
{
NSLog(@"+[BFPerson initialize]");
}
@end
@implementation BFBoy
+ (void)initialize
{
NSLog(@"+[BFBoy initialize]");
}
@end
复制代码
调用
[BFBoy alloc];
复制代码
此时打印以下:
13:44:35.437131+0800 Category[3527:4997267] +[BFPerson initialize]
13:44:35.437240+0800 Category[3527:4997267] +[BFBoy initialize]
+initialize
,不调用类自己的+initialize
@implementation BFPerson
+ (void)initialize
{
NSLog(@"+[BFPerson initialize]");
}
@end
@implementation BFPerson (Work)
+ (void)initialize
{
NSLog(@"+[BFBoy initialize]");
}
@end
复制代码
调用:
[BFBoy alloc];
复制代码
打印输出
13:45:35.437121+0800 Category[3527:4997267] +[BFPerson initialize]--Wrok Cat
initialize
多个子类均未实现,但父类实现+initialize
方法,会屡次调用+initialize
。
@implementation BFPerson
+ (void)initialize
{
NSLog(@"+[BFPerson initialize]");
}
@end
@implementation BFBoy
@end
@implementation BFGirl
@end
复制代码
调用:
[BFBoy alloc];
[BFGirl alloc];
复制代码
打印输出
13:52:44.968194+0800 Category[3786:5010745] +[BFPerson initialize]
13:52:44.968327+0800 Category[3786:5010745] +[BFPerson initialize]
13:52:44.968427+0800 Category[3786:5010745] +[BFPerson initialize]
类的初始化加载,会先调用父类+initialize
方法,再调用类自己的+initialize
。
//伪代码
BOOL personInitialized = NO;
BOOL boyInitialized = NO;
if (!boyInitialized) {
//1. 父类调用
if (!personInitialized) {
objc_msgSend([BFPerson class], @selector(initialize));
personInitialized = YES;
}
//2. 类自己调用
objc_msgSend([BFBoy class], @selector(initialize));
boyInitialized = YES;
}
复制代码
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
....
//须要初始化,且类未进行初始化
if (initialize && !cls->isInitialized()) {
//initialize
_class_initialize (_class_getNonMetaClass(cls, inst));
}
....
}
复制代码
+initialize
调用void _class_initialize(Class cls)
{
// 1. 先调用父类 initialization
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
....
//2. 给类发送initialize消息
callInitialize(cls);
....
//3. 完成类的初始化
lockAndFinishInitializing(cls, supercls);
...
}
复制代码