0%

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

上一篇我们主要探索了dyld的链接加载,本篇开始我们探索运行时类的加载过程,本篇只是引子。

_objc_init

首先看一下_objc_init方法的源码:

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
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
//读取影响运行时的环境变量
environ_init();
//关于线程key的绑定
tls_init();
//运行C ++静态构造函数
static_init();
//runtime运行时环境初始化
runtime_init();
//初始化libobjc的异常处理系统
exception_init();
#if __OBJC2__
//缓存条件初始化
cache_t::init();
#endif
//启动回调机制
_imp_implementationWithBlock_init();
//dyld 注册的地方
//map_images:dyld将image镜像文件加载进内存时,会触发该函数
//load_images:dyld初始化image会触发该函数
//unmap_image:dyld将image移除时会触发该函数
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}

我们可以看到,_objc_init主要是执行一些初始化方法,包括

  • environ_init():读取影响运行时的环境变量,如果需要可以打印环境变量提供帮助。
  • tls_init():关于线程key的绑定,例如每线程数据的析构函数。
  • static_init():运行C++静态构造函数。
  • runtime_init():runtime运行时环境的初始化,后面我们详细分析。
  • exception_init():初始化libobjc的异常处理系统。
  • cache_t::init():缓存条件初始化。
  • _imp_implementationWithBlock_init():启动回调机制。
  • _dyld_objc_notify_register:dyld的注册。

####environ_init 环境变量初始化

environ_init的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void environ_init(void) 
{
///省略代码
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
///这里如果满足条件 会打印所有的环境变量
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}

可以看出来就是一些环境变量的初始化,参看这段代码,我们可以打印环境变量。

  • 将条件去掉直接调用for循环

    1
    2
    3
    4
    5
    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
    const option_t *opt = &Settings[i];
    _objc_inform("%s: %s", opt->env, opt->help);
    _objc_inform("%s is set", opt->env);
    }
  • 通过终端命令export OBJC_hrlp = 1,打印环境变量

我们平时项目中可能会有几个环境变量可以在我们的xcode配置(target -- Edit Scheme -- Run --Arguments -- Environment Variables)一下对应的值,达到修改环境变量的目的:

  • DYLD_PRINT_STATISTICS:设置 DYLD_PRINT_STATISTICSYES,控制台就会打印 App 的加载时长,包括整体加载时长和动态库加载时长,即main函数之前的启动时间(查看pre-main耗时),可以通过设置了解其耗时部分,这个我们做启动优化会用到。
  • OBJC_DISABLE_NONPOINTER_ISA:不使用nonpointer isa(nonpointer isa指针地址 末尾为1 ),生成的都是普通的isa,这个我们项目里一般不会改,探索源码的时候可以尝试查看两种isa结构的区别。
  • OBJC_PRINT_LOAD_METHODS:打印 ClassCategory+ (void)load 方法的调用信息,启动优化也可以参考,因为load方法过多也会使启动变慢。

tls_init:线程key的绑定

主要作用是本地线程池的初始化以及析构,源码:

1
2
3
4
5
6
7
8
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}

static_init 运行系统级别的C++静态构造函数

主要是运行系统级别的C++静态构造函数,在dyld调用我们的静态构造函数之前,libc调用_objc_init方法,即系统级别的C++构造函数 先于 自定义的C++构造函数 运行

1
2
3
4
5
6
7
8
9
10
11
12
13
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
UnsignedInitializer init(offsets[i]);
init();
}
}

runtime_init 运行时环境初始化

主要是运行时的初始化,主要分为两部分:分类初始化类的表初始化,我们下一篇会详细探索。

1
2
3
4
5
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}

exception_init 异常初始化

主要是初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理

1
2
3
4
5
6
7
8
9
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
  • _objc_terminate的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    static void _objc_terminate(void)
    {
    if (PrintExceptions) {
    _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
    // No current exception.
    (*old_terminate)();
    }
    else {
    // There is a current exception. Check if it's an objc exception.
    @try {
    __cxa_rethrow();
    } @catch (id e) {
    // It's an objc object. Call Foundation's handler, if any.
    (*uncaught_handler)((id)e);
    (*old_terminate)();
    } @catch (...) {
    // It's not an objc object. Continue to C++ terminate.
    (*old_terminate)();
    }
    }
    }

    crash的时候会执行_objc_terminate方法,最后会执行uncaught_handler抛出异常。

  • uncaught_handler:

    1
    2
    3
    4
    5
    6
    7
    8
    objc_uncaught_exception_handler 
    objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
    {
    // fn为设置的异常句柄 传入的函数,为外界给的
    objc_uncaught_exception_handler result = uncaught_handler;
    uncaught_handler = fn; //赋值
    return result;
    }

    主要用来处理异常,在我们的App中可以添加一个异常句柄NSSetUncaughtExceptionHandler来处理异常。

cache_init:缓存初始化

主要就是缓存初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void cache_t::init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
mach_msg_type_number_t count = 0;
kern_return_t kr;

while (objc_restartableRanges[count].location) {
count++;
}

kr = task_restartable_ranges_register(mach_task_self(),
objc_restartableRanges, count);
if (kr == KERN_SUCCESS) return;
_objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}

_imp_implementationWithBlock_init:启动回调机制

该方法主要是启动回调机制,看代码iOS端没做任何处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
// programs (most notably QtWebEngineProcess used by older versions of
// embedded Chromium) enable a highly restrictive sandbox profile which
// blocks access to that dylib. If anything calls
// imp_implementationWithBlock (as AppKit has started doing) then we'll
// crash trying to load it. Loading it here sets it up before the sandbox
// profile is enabled and blocks it.
//
// This fixes EA Origin (rdar://problem/50813789)
// and Steam (rdar://problem/55286131)
if (__progname &&
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
strcmp(__progname, "Steam Helper") == 0)) {
Trampolines.Initialize();
}
#endif
}

_dyld_objc_notify_register:dyld注册

这个方法我们上一篇有提到过,主要是

  • 仅供objc运行时使用
  • 注册处理程序,以便在映射、取消映射和初始化objc图像时调用
  • dyld将会通过一个包含objc-image-info的镜像文件的数组回调mapped函数

三个参数的作用:

map_images:dyld将image(镜像文件)加载进内存时,会触发该函数

load_image:dyld初始化image会触发该函数

unmap_image:dyld将image移除时,会触发该函数

我们_objc_init调用的方法有了初步认识,接下来我们开始探索类的加载。

_read_images

_read_images的引入

_dyld_objc_notify_register第一个参数是&map_images,所以我们从map_images开始探索。

1
2
3
4
5
6
7
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}

map_images比较简单,调用map_images_nolock,我们继续探索。

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;

// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
}

if (PrintImages) {
_objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}


// Find all images with Objective-C metadata.
hCount = 0;

// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];

auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}

if (mhdr->filetype == MH_EXECUTE) {
// Size some data structures based on main executable's size
#if __OBJC2__
// If dyld3 optimized the main executable, then there shouldn't
// be any selrefs needed in the dynamic map so we can just init
// to a 0 sized map
if ( !hi->hasPreoptimizedSelectors() ) {
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
}
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif

#if SUPPORT_GC_COMPAT
// Halt if this is a GC app.
if (shouldRejectGCApp(hi)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"Objective-C garbage collection "
"is no longer supported.");
}
#endif
}

hList[hCount++] = hi;

if (PrintImages) {
_objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
hi->fname(),
mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
hi->info()->isReplacement() ? " (replacement)" : "",
hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
hi->info()->optimizedByDyld()?" (preoptimized)":"");
}
}
}

// Perform one-time runtime initialization that must be deferred until
// the executable itself is found. This needs to be done before
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later.
if (firstTime) {
sel_init(selrefCount);
arr_init();

#if SUPPORT_GC_COMPAT
// Reject any GC images linked to the main executable.
// We already rejected the app itself above.
// Images loaded after launch will be rejected by dyld.

for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[i];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"%s requires Objective-C garbage collection "
"which is no longer supported.", hi->fname());
}
}
#endif

#if TARGET_OS_OSX
// Disable +initialize fork safety if the app is too old (< 10.13).
// Disable +initialize fork safety if the app has a
// __DATA,__objc_fork_ok section.

// if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) {
// DisableInitializeForkSafety = true;
// if (PrintInitializing) {
// _objc_inform("INITIALIZE: disabling +initialize fork "
// "safety enforcement because the app is "
// "too old.)");
// }
// }

for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[i];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {
DisableInitializeForkSafety = true;
if (PrintInitializing) {
_objc_inform("INITIALIZE: disabling +initialize fork "
"safety enforcement because the app has "
"a __DATA,__objc_fork_ok section");
}
}
break; // assume only one MH_EXECUTE image
}
#endif
}
if (hCount > 0) {
///核心方法
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}

firstTime = NO;

// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}

map_images_nolock方法比较长,很大一部分代码是读取镜像的准备,核心调用是_read_images方法。

_read_images分析

_read_images有三百多行代码,一行行读容易迷失,我们先将方法内的{}语句隐藏,从全局看这个方法都做了什么:

截屏2021-07-17 12.56.13

这个方法有个特点就是没执行完一个代码块都会执行ts.log()函数打印代码段都做了什么,这样子我们大体知道_read_images的主流程:

  • 条件控制进⾏⼀次的加载
  • 修复预编译阶段的 @selector 的混乱问题
  • 错误混乱的类处理
  • 修复重映射⼀些没有被镜像⽂件加载进来的类
  • 修复⼀些消息
  • 当我们类⾥⾯有协议的时候 : readProtocol
  • 修复没有被加载的协议
  • 分类处理
  • 类的加载处理
  • 没有被处理的类 优化那些被侵犯的类

first time tasks即:条件控制进⾏⼀次的加载

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
70
71
72
73
74
75
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;

#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.

# if SUPPORT_INDEXED_ISA
// Disable nonpointer isa if any image contains old Swift code
for (EACH_HEADER) {
if (hi->info()->containsSwift() &&
hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app or a framework contains Swift code "
"older than Swift 3.0");
}
break;
}
}
# endif

# if TARGET_OS_OSX
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
// if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_11)) {
// DisableNonpointerIsa = true;
// if (PrintRawIsa) {
// _objc_inform("RAW ISA: disabling non-pointer isa because "
// "the app is too old.");
// }
// }

// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (EACH_HEADER) {
if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app has a __DATA,__objc_rawisa section");
}
}
break; // assume only one MH_EXECUTE image
}
# endif

#endif

if (DisableTaggedPointers) {
disableTaggedPointers();
}
///初始化TaggedPointer混淆
initializeTaggedPointerObfuscator();

if (PrintConnecting) {
_objc_inform("CLASS: found %d classes during launch", totalClasses);
}
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
// objc::unattachedCategories.init(32);
// objc::allocatedClasses.init();
//负载因子
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
//创建一张类的总表,包含所有的类
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}

主要做了两件事情

  • 初始化小对象(TaggedPointer)
  • 创建所有类的总表,注意这个表和runtime_initallocatedClassess的表不一样,这里是所有类的表,而allocatedClassess是已经实现(allocated)的类的表。

fix up selector references即:修复预编译阶段的 @selector 的混乱问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;

bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
ts.log("IMAGE TIMES: fix up selector references");

selecotr是类的名字+地址。

  • SEL *sels = _getObjc2SelectorRefs(hi, &count);是从Mach-o文件读取的
  • SEL sel = sel_registerNameNoLock(name, isBundle);是从dyld链接之后获取的
  • 如果两个sel名字相等,地址不同就需要fix up

discover 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
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
//从mach-o读取类
classref_t const *classlist = _getObjc2ClassList(hi, &count);

bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

for (i = 0; i < count; i++) {
//cls 目前没有名字
Class cls = (Class)classlist[i];
//关联类cls的名字
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
ts.log("IMAGE TIMES: discover classes");

修复未处理的将来的类。给类关联上名字。下面我们重点探索一下readClass:

readClass

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->nonlazyMangledName();
///以下代码 为笔者添加 方便调试到自定义的类(因为所以类都会执行这里,我们只关心我们定义的类)
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
//打印类名
printf("%s -: 要研究的类: - %s\n",__func__,mangledName);
}

if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->setSuperclass(nil);
return nil;
}

cls->fixupBackwardDeployingStableSwift();

Class replacing = nil;
if (mangledName != nullptr) {
if (Class newCls = popFutureNamedClass(mangledName)) {
//断点调试 这里并没有执行
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.

if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}

class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));

// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());

rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);

addRemappedClass(cls, newCls);

replacing = cls;
cls = newCls;
}
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
///给类添加名字
addNamedClass(cls, mangledName, replacing);
} else {
///元类也处理
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
///添加到class表
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}

我们从返回值开始看cls,cls也是入参,经过readClass之后有了名字,主要作用就是对类及元类赋值名字并放入方法表中。

addNamedClass 添加方法名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);

// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));

// wrong: constructed classes are already realized when they get here
// ASSERT(!cls->isRealized());
}

addClassTableEntry

将类添加到表里面,如果addMeta为真,并且将当前类的元类也添加到所有的表中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

static void
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();

// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
//// 该类允许通过共享缓存或数据段成为已知类,但不允许已经在动态表中。
auto &set = objc::allocatedClasses.get();

ASSERT(set.find(cls) == set.end());

if (!isKnownClass(cls))
set.insert(cls);
if (addMeta)
//元类插入到所有类表中
addClassTableEntry(cls->ISA(), false);
}

总结

本篇我们主要是对objc_init的流程做了一个简单的梳理,分析了readClass方法的作用,下一篇我们开始详细分析类的加载过程。