0%

iOS底层探索-类的加载(中)

上一篇我们探索到了readClass函数,只是对函数名字进行了赋值,并没有对rorw进行操作,本篇我们就继续探索_read_images函数剩下的调用。

realizeClass的引入

因为我们探索的目的是的加载,我们先忽略protocolcategories的地方。为了调试代码,我们首先还是先创建一个JSPerson类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface JSPerson : NSObject
@property (nonatomic, strong) NSString *nickName;

- (void)say1;
- (void)say2;

+ (void)sayHappy;
@end

#import "JSPerson.h"
@implementation JSPerson

- (void)say1{
NSLog(@"JSPerson say : %s",__func__);
}
- (void)say2{
NSLog(@"JSPerson say : %s",__func__);
}

+ (void)load{
NSLog(@"load");
}
+ (void)sayHappy{
NSLog(@"JSPerson say : %s",__func__);
}
@end

接下来我们继续看_read_images函数,发现和类相关的地方有两个地方,realize non-lazy classesrealize future classes,我们在两段代码中加入我们调试的代码,为了观察我们自定义类的加载情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName);
}

addClassTableEntry(cls);

if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
printf("%s -: realize future classes要研究的类: - %s\n",__func__,mangledName);
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");

在两行printf的地方打断点,运行源码程序,看是否执行到断点位置。发现打印了_read_images -: non-lazy classes要研究的类: - JSPerson,代码执行到了non-lazy classes,里面类加载的核心代码在realizeClassWithoutSwift函数,我们继续探索realizeClassWithoutSwift函数。

realizeClassWithoutSwift分析

操作之前的ro

我们从上到下依次阅读代码,定位到auto ro = (const class_ro_t )cls->data();,因为roclean Memory里的数据我们比较敏感,我们在图示位置打断点:

ro赋值之前

使用LLDB调试程序,查看ro里的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(lldb) p ro
(const class_ro_t *) $0 = 0x00000001000080c0
(lldb) p *$0
(const class_ro_t) $1 = {
flags = 0
instanceStart = 8
instanceSize = 16
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "JSPerson" {
Value = 0x0000000100003db0 "JSPerson"
}
}
baseMethodList = 0x0000000100008108
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008170
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008198
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $1.baseMethodList
(void *const) $2 = 0x0000000100008108
(lldb) p *$2
(lldb)

通过打印我们发现此时ro里的baseMethodList为空,目前还不清楚什么时候赋值的,我们继续探索。

rw的赋值

接下来就是对rw的赋值,注意rwdirty Memory

1
2
3
4
5
6
7
8
9
10
11
12
13
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}

isa和superClass赋值

后面的代码就是对isasuperClass的赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
///元类 不是non pointer ISA
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;

if (DisableNonpointerIsa) {
//如果我们设置变量不使用 non pointer ISA 也会是纯的isa
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}

if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);

realizeClassWithoutSwift函数最后会调用methodizeClass,我们下一小节探索methodizeClass

methodizeClass分析

methodizeClass顾名思义就是对方法的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
if (!isMeta) {
printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName);
}
}
// Install methods and properties that the class implements itself.
//取出方法列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name()));
}
ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
}
#endif
}

断点进入图示位置,此时方法列表还是不能打印

methodList打印

prepareMethodLists

我们继续探索,后面执行prepareMethodLists函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
static void 
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
// There exist RR/AWZ/Core special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ/Core:
// default RR/AWZ/Core cannot be set before setInitialized().
// Therefore we need not handle any special cases here.
if (baseMethods) {
ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
} else if (cls->cache.isConstantOptimizedCache()) {
cls->setDisallowPreoptCachesRecursively(why);
} else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
cls->setDisallowPreoptInlinedSelsRecursively(why);
}
#endif
}
// Add method lists to array.
// Reallocate un-fixed method lists.
// The new methods are PREPENDED to the method list array.

for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
// Fixup selectors if necessary
if (!mlist->isFixedUp()) {
//核心代码
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}

// If the class is initialized, then scan for method implementations
// tracked by the class's flags. If it's not initialized yet,
// then objc_class::setInitialized() will take care of it.
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}

核心调用是fixupMethodList函数。

fixupMethodList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static void 
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);

// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
// Sort by selector address.
// Don't try to sort small lists, as they're immutable.
// Don't try to sort big lists of nonstandard size, as stable_sort
// won't copy the entries properly.
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}

核心的代码是stable_sort,我们分别打印排序前后方法列表,如图示

打印前后排序

注意:这里一定要先在realizeClassWithoutSwift判断好是我们要研究的JSPerson类,然后再看打印结果,否则系统类也会有很多打印,影响我们分析。

1
2
3
4
5
6
7
8
9
methodizeClass -: non-lazy classes要研究的类: - JSPerson
****************sort之前 : say1 - 0x100003dda
sort之前 : say2 - 0x100003ddf
sort之前 : nickName - 0x7fff73fb8a1c
sort之前 : setNickName: - 0x7fff73fb8362
****************sort之后 : say1 - 0x100003dda
sort之后 : say2 - 0x100003ddf
sort之后 : setNickName: - 0x7fff73fb8362
sort之后 : nickName - 0x7fff73fb8a1c

通过上面打印结果:

  • 排序前:say1 - 0x100003ddasay2 - 0x100003ddfnickName - 0x7fff73fb8a1csetNickName: - 0x7fff73fb8362
  • 排序后:say1 - 0x100003ddasay2 - 0x100003ddfsetNickName: - 0x7fff73fb8362nickName - 0x7fff73fb8a1c
  • 排序是根据地址由低到高排序的。

小结

到目前为止,类的加载流程是:_read_images->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList

懒加载类和非懒加载类

我们前面探索的其实都是非懒加载的类懒加载类非懒加载的类的区别就是是否实现了load方法

非懒加载

通过上面的分析,我们已经很清楚了,是在_objc_init方法里加载的,也就是程序启动的时候。这也就是为什么load方法过多,会影响我们应用的启动速度

懒加载类

因为非懒加载类效率低,会影响我们的启动速度,那懒加载类是什么时候加载的呢?我们删掉JSPerson类的load方法,然后在main函数中实例化一个JSPerson实例

1
2
3
4
5
6
7
8
int main(int argc, const char * argv[]) {
@autoreleasepool {
JSPerson *p = [JSPerson alloc];
[p say1];
NSLog(@"Hello, World!");
}
return 0;
}

我们首先在main方法里添加断点,执行程序。走到main函数之后,然后再在realizeClassWithoutSwift添加断点:

懒加载类断点

断点走进来之后我们bt打印调用栈信息:

懒加载类

发现调用是从lookUpImpOrForward开始。

所以我们的结论是懒加载的类是在第一次被使用的时候加载的。

总结

  • 非懒加载类:程序运行时加载,_read_images->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList
  • 懒加载类:第一次使用时加载,lookUpImpOrForward->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList

我们开发中经常会写分类,它是什么时候加载的及加载的流程,我们下一篇再探索。