懒加载类和非懒加载类的区分很简单,就是看类有没有实现load方法
非懒加载类:
在App启动时就开始对其进行实现,
因为这个类实现了load方法,load方法是在启动的时候就会被调用的,既然要调用方法,就需要对类进行实现,这个还是很好理解的
⚠️:还有2中情况会导致类即使不发送消息,也会提前实现
1、子类实现了load方法: 如果一个类(SuperA)有子类(ChildB),子类实现了load方法,那么这个类(SuperA)也可以认为是一个非懒加载类。
因为在进行类的加载的时候,SuperA 并不在非懒加载类的列表(下面的代码有打印非懒加载类列表),但是在进行ChildB初始化的时候,有一个递归加载,加载其父类,也就是说,在这个阶段SuperA也已经开始实现了
2、分类实现了load方法 在 这片文章的 2.1.2 部分讲了具体的实现流程
@interface JEPerson : NSObject
@end
@implementation JEPerson
+ (void)load
{
}
@end
非懒加载类
没有实现load方法,在第一次对其发送消息的时候(调用方法之前),才初始化
目的:降低启动的时间,减少启动工作量。比如一个项目中有10000个类,没有必要在启动的时候就全部加载出来,因为这个时候根本不需要用到它。
验证哪些类是非懒加载类?
_objc_init()
->
_dyld_objc_notify_register(&map_images, load_images, unmap_image); 中的map_images
->
map_images_nolock()
->
_read_images()
->
在 // Realize non-lazy classes (for +load methods and static instances)
下面的
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
//添加这行代码 打印非懒加载类名
printf("non-lazy classes: %s\n",cls->mangledName());
if (!cls) continue;
.....
}
创建几个类 并实现部分类的load方法 运行并查看打印
......
non-lazy classes: Protocol
non-lazy classes: __NSUnrecognizedTaggedPointer
non-lazy classes: NSObject // 请注意:NSObject是一个非懒加载类
non-lazy classes: __NSArray0
......
non-lazy classes: NSConstantDate
non-lazy classes: __NSPlaceholderDictionary
non-lazy classes: NSConstantIntegerNumber
non-lazy classes: NSConstantFloatNumber
non-lazy classes: NSConstantDoubleNumber
non-lazy classes: NSApplication
non-lazy classes: NSBinder
non-lazy classes: NSColorSpaceColor
non-lazy classes: NSNextStepFrame
non-lazy classes: _DKEventQuery
non-lazy classes: JEStudent ///< 这是实现了load方法的类
非懒加载类的初始化流程
还是进入read_images()
的 非懒加载类的方法这里
Realize non-lazy classes (for +load methods and static instances)
里面的
realizeClassWithoutSwift()
方法
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) { // 已经初始化过的类才会进入这里
...
} else {
// 第一次初始化类 创建一个新的rw 并对rw的ro赋值
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
//递归初始化父类和元类
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
//..... 修改flags标记、关联父类、元类 等一系列操作
return cls;
}
懒加载类的初始化流程
我们说懒加载类是在第一次发送消息(调用方法)的时候才初始化。
具体看看是怎样的流程。
在调用方法的时候 底层会首先找到 lookUpImpOrForward ()
方法
怎么知道是调用这个方法呢?
方法一、底层源码看汇编 慢慢分析
方法二、汇编断点追踪 打开汇编模式(debug->always show disassembly) 在sendMsg call/jmp 通过in 进入查看,最终能看到 lookUpImpOrForward at objc-runtime-new.mm:5989
//创建一个 JEPerson类 并且没有实现load方法 并且没有子类
JEPerson *per = [JEPerson alloc]; //在这行打上断点
运行,来到这行断点之后,在 lookUpImpOrForward
里面也打上断点
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
❌❌
注意点:这里的cls 是元类,因为这里调用的是类方法 + (id)alloc
如何去验证?
往下走,在`realizeClassWithoutSwift`方法中 有一个 isMeta的判断,那里为true
❌❌
.......
// 因为类还没有实现,所以会进入这个判断
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
//如果类还没有初始化
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
......
}
//继续追踪进去,
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
.....
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
realizeClassWithoutSwift(cls, nil);
if (!leaveLocked) lock.unlock();
}
......
}
调用
alloc
方法之后的流程
lookUpImpOrForward()
-> realizeClassMaybeSwiftAndLeaveLocked() 传入的是元类,对元类进行实现
-> realizeClassWithoutSwift
->
出来之后 进入lookUpImpOrForward -> initializeAndLeaveLocked 对类进行初始化
->realizeClassWithoutSwift
简单点说:元类进行一次realizeClassWithoutSwift
方法 之后 在对 类进行一次 realizeClassWithoutSwift
方法
网友评论