_objc_init源码

这个liobjc是iOS中的一个底层动态库,他是在libobjc.A.dylib被加载时由系统启动流程间触发的。
- 进程启动
- dyld 开始装载主程序和依赖动态库
libSystem初始化libSystem很早就调用_objc_init()_objc_init()里把 ObjC runtime 先启动起来,并向 dyld 注册 image 回调- 之后dyld 再继续走库初始化、静态构造器、image 通知、
+load等后续流程
可以看到这个方法中进行了许多初始化函数
environ_init
objc
void environ_init(void)
{
#if !TARGET_OS_EXCLAVEKIT
if (issetugid()) {
// All environment variables are silently ignored when setuid or setgid
// This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
return;
}
// Turn off autorelease LRU coalescing by default for apps linked against
// older SDKs. LRU coalescing can reorder releases and certain older apps
// are accidentally relying on the ordering.
// rdar://problem/63886091
// if (!dyld_program_sdk_at_least(dyld_fall_2020_os_versions))
// DisableAutoreleaseCoalescingLRU = On;
// class_rx_t pointer signing enforcement is *disabled* by default unless
// this OS feature is enabled, but it can be explicitly enabled by setting
// the environment variable, for testing.
// if (!os_feature_enabled_simple(objc4, classRxSigning, false))
// DisableClassRXSigningEnforcement = On;
// Faults for class_ro_t pointer signing enforcement are disabled by
// default unless this OS feature is enabled.
// if (!os_feature_enabled_simple(objc4, classRoSigningFaults, false))
// DisableClassROFaults = On;
#if TARGET_OS_OSX || TARGET_OS_SIMULATOR
// if (!os_feature_enabled_simple(objc4, autoreleaseFaultsMacOS, false))
// DisableFaults = On;
#endif
#endif // !TARGET_OS_EXCLAVEKIT
bool PrintHelp = false;
bool PrintOptions = false;
bool maybeMallocDebugging = false;
// Scan environ[] directly instead of calling getenv() a lot.
// This optimizes the case where none are set.
char **envp = NULL;
#if TARGET_OS_EXCLAVEKIT
if (_objc_test_get_environ)
envp = _objc_test_get_environ();
#else
envp = *_NSGetEnviron();
#endif
if (!envp)
return;
for (char **p = envp; *p != nil; p++) {
if (0 == strncmp(*p, "Malloc", 6) || 0 == strncmp(*p, "DYLD", 4) ||
0 == strncmp(*p, "NSZombiesEnabled", 16))
{
maybeMallocDebugging = true;
}
if (0 != strncmp(*p, "OBJC_", 5)) continue;
if (0 == strncmp(*p, "OBJC_HELP=", 10)) {
PrintHelp = true;
continue;
}
if (0 == strncmp(*p, "OBJC_PRINT_OPTIONS=", 19)) {
PrintOptions = true;
continue;
}
if (0 == strncmp(*p, "OBJC_DEBUG_POOL_DEPTH=", 22)) {
SetPageCountWarning(*p + 22);
continue;
}
const char *value = strchr(*p, '=');
if (!*value) continue;
value++;
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
#if !TARGET_OS_EXCLAVEKIT
if (opt->internal
&& !os_variant_allows_internal_security_policies("com.apple.obj-c"))
continue;
#endif // !TARGET_OS_EXCLAVEKIT
if ((size_t)(value - *p) == 1+opt->envlen &&
0 == strncmp(*p, opt->env, opt->envlen))
{
if (strcasecmp(value, "fatal") == 0
|| strcasecmp(value, "halt") == 0)
*opt->var = Fatal;
else if (strcasecmp(value, "yes") == 0
|| strcasecmp(value, "warn") == 0
|| strcasecmp(value, "true") == 0
|| strcasecmp(value, "on") == 0
|| strcasecmp(value, "y") == 0
|| strcmp(value, "1") == 0)
*opt->var = On;
else
*opt->var = Off;
break;
}
}
}
#if !TARGET_OS_EXCLAVEKIT
// Special case: enable some autorelease pool debugging
// when some malloc debugging is enabled
// and OBJC_DEBUG_POOL_ALLOCATION is not set to something other than NO.
if (maybeMallocDebugging) {
const char *insert = getenv("DYLD_INSERT_LIBRARIES");
const char *zombie = getenv("NSZombiesEnabled");
const char *pooldebug = getenv("OBJC_DEBUG_POOL_ALLOCATION");
if ((getenv("MallocStackLogging")
|| getenv("MallocStackLoggingNoCompact")
|| (zombie && (*zombie == 'Y' || *zombie == 'y'))
|| (insert && strstr(insert, "libgmalloc")))
&& !pooldebug) {
DebugPoolAllocation = On;
}
}
// if (!os_feature_enabled_simple(objc4, preoptimizedCaches, true)) {
// DisablePreoptCaches = On;
// }
#endif // !TARGET_OS_EXCLAVEKIT
// 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 !TARGET_OS_EXCLAVEKIT
if (opt->internal
&& !os_variant_allows_internal_security_policies("com.apple.obj-c"))
continue;
#endif // !TARGET_OS_EXCLAVEKIT
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions) {
switch (*opt->var) {
case Off:
break;
case On:
_objc_inform("%s is set", opt->env);
break;
case Fatal:
_objc_inform("%s is fatal", opt->env);
break;
}
}
}
}
}
这段代码主是进行了一些环境变量的配置。
DYLD_PRINT_STATISTICS:设置DYLD_PRINT_STATISTICS为YES,控制台就会打印 App 的加载时长,包括整体加载时长和动态库加载时长,即main函数之前的启动时间(查看pre-main耗时),可以通过设置了解其耗时部分,并对其进行启动优化。OBJC_DISABLE_NONPOINTER_ISA:禁用生成相应的nonpointer isa(nonpointer isa指针地址末尾为1),生成的都是普通的isaOBJC_PRINT_LOAD_METHODS:打印Class及Category的+ (void)load方法的调用信息NSDoubleLocalizedStrings:项目做国际化本地化(Localized)的时候是一个挺耗时的工作,想要检测国际化翻译好的语言文字UI会变成什么样子,可以指定这个启动项。可以设置NSDoubleLocalizedStrings为YES。NSShowNonLocalizedStrings:在完成国际化的时候,偶尔会有一些字符串没有做本地化,这时就可以设置NSShowNonLocalizedStrings为YES,所有没有被本地化的字符串全都会变成大写。

配置OBJC_PRINT_LOAD_METHODS可以监控所有的load方法从而处理启动优化
tis_init
这个方法可能是因为版本原因,上面没有显示,实际上就是对本地线程池的一个初始化。
objc
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS//本地线程池,用来进行处理
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);//初始init
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);//析构
#endif
}
static_init
objc
__attribute__((noinline))
static void static_init()
{
size_t count;
auto offsets = getLibobjcInitializerOffsets(&count);
for (size_t i = 0; i < count; i++) {
UnsignedInitializer init(offsets[i]);
init();
}
#if DEBUG
if (count == 0)
_objc_inform("No static initializers found in libobjc. This is unexpected for a debug build. Make sure the 'markgc' build phase ran on this dylib. This process is probably going to crash momentarily due to using uninitialized global data.");
#endif
}
在dylb调用静态构造函数之前,libc会自动调用objc_init,导致其中的一些C++静态构造函数需要我们自己去实现。主要运行的是运行系统级别的C++静态静态构造函数,系统级别的C++构造函数先与自定义的C++构造函数
runtime_init
运行时初始化,主要是类的初始化和类的表初始化
objc
void runtime_init(void)
{
objc::Scanner::init();
objc::disableEnforceClassRXPtrAuth = DisableClassRXSigningEnforcement;//安全配置
objc::unattachedCategories.init(32);//初始化一个全局表,存储待附加分类
objc::allocatedClasses.init();//把"动态分配类集合"先建好,后面给 objc_allocateClassPair、类合法性检查、类销毁流程使用。
}
exception_init
主要是初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理
objc
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
当有crash发生时,会来到_objc_terminate方法,最后走到uncaught_handler抛出异常
objc
static void (*old_terminate)(void) = nil;
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)();
}
}
}
在app层会传入一个函数用于处理异常,便于调用函数,然后回到原有的app层中
objc
objc_uncaught_exception_handler
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
{
objc_uncaught_exception_handler result = uncaught_handler;
uncaught_handler = fn;
return result;
}
- Mach异常(内核层)
- Unix信号(系统层)//主要的crash在这个层级
- NSException(应用层)//未捕获的OC"异常,runtime会调用abort发送SIGABRT信号造成crash
针对应用级异常,可以通过注册异常捕获函数,即NSSetUncaughtExceptionHandler机制实现线程的保活,并收集上床崩溃日志
在实际开发中可以针对Crash进行一个拦截处理,即app代码中给出一个异常句柄NSSetUncaughtExceptionHandler,传入一个函数给系统,当异常发生后,调用函数(函数中可以线程保活、收集并上传崩溃日志),然后回到原有的app层中,其本质就是一个回调函数。

cache_init
初始化缓存。具体代码过长,这里就不粘出来了。
_imp_implementationWithBlock_init
负责处理启动回调机制,这个方法的作用是将一个block转换为一个OC得方法实现IMP。负责tramPolin的动态库初始化,正常情况是懒加载的。做一个兼容性处理。说白了就是趁着sanbox还没收紧之前,将后面必须的库先装进内存
objc
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();//初始化trampoline子系统,提前加载libobjc-trampolines.dylib
}
#endif
}
_dyld_objc_notify_register
注册dyld回调函数,仅供objc运行时使用,注册处理程序,便于在映射、取消映射和初始化dyld图像时调用。
dyld将通过一个包含objc-image-info的镜像文件的数组回调mapped函数。
三个参数:
- map_images:dyld将image镜像文件加载进入内存中,会触发该函数
- Laod-image:dyld初始化image时会调用
- Unman_image:dyld移除image时触发该函数
objc
_dyld_objc_callbacks_v2 callbacks = {
2, // version
&map_images,
&load_images,
unmap_image,
_objc_patch_root_of_class
};
_dyld_objc_register_callbacks((_dyld_objc_callbacks*)&callbacks);


总结
App 启动时,内核先加载 dyld,dyld 负责加载主程序和依赖动态库。dyld 在初始化过程中会执行各个 image 的 initializer,其中 libSystem 的初始化会触发 libdispatch 和 libobjc 的初始化。libobjc 中的 _objc_init 会调用 _dyld_objc_notify_register,把 map_images、load_images、unmap_image 三个回调注册给 dyld。之后 dyld 在加载 Mach-O image 时,会通过这些回调通知 Objective-C Runtime。map_images 负责读取 Mach-O 中的 ObjC 元数据,例如类、分类、协议,并完成注册;load_images 负责调用类和分类的 +load 方法;unmap_image 则用于 image 卸载时清理。所有这些工作完成后,dyld 才会把控制权交给 main 函数。
类的加载流程
map_images是引用类型,外界变了,跟着变。load_images是值类型,不传递值

map_images:加载镜像文件到内存
主要作用是将Mach-O中的类信息加载到内存中。当OC的runtime被dyld通知有新的Mach-O镜像被映射进进程时调用入口函数。处理刚刚加载进来的镜像,并将其中的OC元数据注册进runtime
objc
void
map_images(unsigned count, const struct _dyld_objc_notify_mapped_info infos[])
{
bool takeEnforcementDisableFault;
{
mutex_locker_t lock(runtimeLock);
map_images_nolock(count, infos, &takeEnforcementDisableFault);
}
if (takeEnforcementDisableFault) {
if (DebugClassRXSigning == Fatal)
_objc_fatal("class_rx signing mismatch");
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
bool objcModeNoFaults = DisableFaults
|| DisableClassROFaults
|| getpid() == 1
|| is_root_ramdisk()
|| !os_variant_has_internal_diagnostics("com.apple.obj-c");
if (!objcModeNoFaults) {
os_fault_with_payload(OS_REASON_LIBSYSTEM,
OS_REASON_LIBSYSTEM_CODE_FAULT,
NULL, 0,
"class_ro_t enforcement disabled",
0);
}
#endif
}
}
我们进入map_images_nolock,只保留关键部分如下:
objc
void map_images_nolock(unsigned mhCount,
const struct _dyld_objc_notify_mapped_info infos[],
bool *disabledClassROEnforcement)
{
static bool firstTime = YES;//判断当前进程是不是第一次执行map_images_nolock,因为OC的初始化不是一开始就全部完成,而是在dyld加载镜像时逐步触发,第一次进来的时候会有一些全局初始化需要实现
static bool executableHasClassROSigning = false;//是否启动class_ro_t指针签名
static bool executableIsARM64e = false;
mapped_image_info mappedInfos[mhCount];
uint32_t hCount = 0;
size_t selrefCount = 0;
int totalClasses = 0;
int unoptimizedTotalClasses = 0;//没有被预优化的类数量,提前处理了一部分Objc元数据的类
*disabledClassROEnforcement = false;//默认没有关闭
// 1. 首次初始化或补充加载分类
if (firstTime) {
preopt_init();//初始化OC得runtime使用dyld预优化数据的环境**
} else {
loadAllCategoriesIfNeeded();//处理一个category的特殊补救逻辑**
}
// 2. 扫描所有新映射镜像,找出包含 Objective-C 元数据的镜像
for (uint32_t i = 0; i < mhCount; i++) {
const headerType *mhdr = (const headerType *)infos[i].mh;
auto hi = addHeader(mhdr,
infos[i].path,
infos[i].sectionLocationMetadata,
totalClasses,
unoptimizedTotalClasses);//检查类中的元数据
if (!hi) {//如果没有直接提条福哦当前镜像
continue;
}
mapped_image_info mappedInfo{hi, infos[i]};//组成一个完整镜像
// 3. 处理主可执行文件相关信息
if (mhdr->filetype == MH_EXECUTE) {
if (mappedInfo.dyldObjCRefsOptimized()) {//是否优化引用
size_t count = 0;
hi->selrefs(&count);
selrefCount += count;
hi->messagerefs(&count);
selrefCount += count;
}
if (hasSignedClassROPointers(hi)) {
executableHasClassROSigning = true;
}
}
mappedInfos[hCount++] = mappedInfo;
// dtrace probe
OBJC_RUNTIME_LOAD_IMAGE(hi->fname(),
mhdr->filetype == MH_BUNDLE,
hi->info()->hasCategoryClassProperties(),
hi->info()->optimizedByDyld());//观察runtime加载行为
}
// 4. 首次运行时初始化
if (firstTime) {
sel_init(selrefCount);//初始化selector系统
arr_init();//初始化自动释放池、引用管理或者相关runtime基础设施有关的结构
const headerType *mainExecutableHeader =
(headerType *)_dyld_get_prog_image_header();
if (mainExecutableHeader &&
mainExecutableHeader->cputype == CPU_TYPE_ARM64 &&
((mainExecutableHeader->cpusubtype & ~CPU_SUBTYPE_MASK)
== CPU_SUBTYPE_ARM64E)) {
executableIsARM64e = true;
}
}
// 5. ARM64e 下检查 class_ro_t 指针签名
if (executableIsARM64e) {
bool shouldWarn = executableHasClassROSigning && DebugClassRXSigning;
for (uint32_t i = 0; i < hCount; i++) {
auto hi = mappedInfos[i].hi;
if (!hasSignedClassROPointers(hi)) {
if (!objc::disableEnforceClassRXPtrAuth) {
*disabledClassROEnforcement = true;
objc::disableEnforceClassRXPtrAuth = 1;
}
if (shouldWarn) {
_objc_inform("%s has un-signed class_ro_t pointers",
hi->fname());
}
}
}
}
// 6. 读取 Objective-C 镜像元数据
if (hCount > 0) {
_read_images(mappedInfos,
hCount,
totalClasses,
unoptimizedTotalClasses);//正式解析,处理类、分类、协议等信息的注册
}
firstTime = NO;
// 7. 执行镜像加载回调
for (auto callback : loadImageCallbacks) {
for (uint32_t i = 0; i < mhCount; i++) {
switch (callback.kind) {
case 1:
callback.func(infos[i].mh);
break;
case 2:
callback.func2(infos[i].mh,
infos[i].sectionLocationMetadata);
break;
default:
_objc_fatal("Corrupt load image callback");
}
}
}
}
我们抽象一下流程,大概如下:
objc
map_images_nolock()
{
if (第一次进入)
初始化 dyld 预优化信息;
else
补充加载 category;
for (每个 dyld 新映射镜像) {
读取 Mach-O header;
if (没有 Objective-C 元数据)
跳过;
记录 ObjC 镜像信息;
if (这是主可执行文件) {
统计 selector/message refs;
检查 class_ro_t 是否启用签名;
}
触发 runtime load image probe;
}
if (第一次进入) {
初始化 selector 表;
初始化 runtime 基础结构;
判断主程序是否为 ARM64e;
}
if (主程序是 ARM64e) {
检查所有 ObjC 镜像的 class_ro_t 指针签名;
if (发现未签名镜像) {
关闭 enforcement;
通知外层可能需要 fault;
}
}
if (存在 ObjC 镜像) {
正式读取并注册 ObjC 元数据;
}
标记不再是第一次;
执行镜像加载回调;
}
_read_images主要的就是加载类信息,主要分为以下几部分:
- 条件控制进行的一次加载
- 修复预编译阶段的@selector混乱问题
- 错误混乱的类处理
- 修复重映射一些没有被镜像文件加载进来的类
- 修复一些消息
- 当类中有协议时,readProtocal读取协议
- 修复没有被加载的协议
- 分类处理
- 类的加载处理
- 没有被处理的类,优化那些被侵犯的类
1.创建表
objc
if (!doneOnce) {
// dtrace probe
OBJC_RUNTIME_FIRST_TIME_START();
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 (auto info : infos) {
if (info.hi->info()->containsSwift() &&
info.hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = On;
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
# if !TARGET_OS_EXCLAVEKIT
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
// Note: we must check for macOS, because Catalyst and Almond apps
// return false for a Mac SDK check! rdar://78225780
if (dyld_get_active_platform() == PLATFORM_MACOS) {
DisableNonpointerIsa = On;
if (PrintRawIsa) {
_objc_inform("RAW ISA: disabling non-pointer isa because "
"the app is too old.");
}
}
# endif
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (auto info : infos) {
if (info.hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (info.hi->hasRawISASection()) {
DisableNonpointerIsa = On;
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();
}
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
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
NXMapTablePrototype namedClassesPrototype = NXStrValueMapPrototype;
#if __has_feature(ptrauth_calls)
// Only set this when we have ptrauth, we use the standard callback
// otherwise.
namedClassesPrototype.hash = namedClassTableHashCallback;
#endif
gdb_objc_realized_classes =
NXCreateMapTable(namedClassesPrototype, namedClassesSize);
// dtrace probe
OBJC_RUNTIME_FIRST_TIME_END();
}
这段代码的主要作用就是为运行时的类名查找表gdb_objc_realized_classes 分配并初始化 NXMapTable,用于保存非预优化的已实现类的类名到类对象映射,表大小按类数量和负载因子预估,在支持指针认证的平台上还会替换专用的哈希回调。(被dyld预优化过的类不会被处理)
2.修复预编译阶段的@selector混乱问题
objc
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (auto info : infos) {
if (info.dyldObjCRefsOptimized()) continue;
bool isBundle = info.hi->isBundle();
SEL *sels = info.hi->selrefs(&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) {//值相同,但是地址可能不相同,需要fix一下
sels[i] = sel;
}
}
}
}
主要是在加载镜像时对未被dyld预优化的selector引用进行修正和唯一化注册。把各个镜像中零散的 selector 引用统一修正为 Runtime 全局唯一的 selector 指针,保证同名方法选择子在整个进程内只有一个标准 SEL 表示,并为后续消息发送、方法查找和比较提供一致性。
3.错误混乱的类处理
objc
// 判断 dyld shared cache 中是否有镜像被外部镜像覆盖。
// 如果存在 override,某些 dyld 预优化结果可能不能完全信任,
// 后面判断是否必须重新读取类时会用到这个信息。
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
// 遍历当前这批待处理的 Mach-O 镜像。
// 每个 info 表示一个镜像的 Objective-C 元数据信息。
for (auto info : infos) {
// 判断当前镜像是否必须由 Objective-C Runtime 重新读取类信息。
//
// 如果返回 false,说明这个镜像已经被 dyld 充分优化,
// Runtime 不需要逐个调用 readClass() 注册类。
//
// 典型情况:系统库、shared cache 中已经预优化过的镜像。
if (! mustReadClasses(info, hasDyldRoots)) {
// 当前镜像优化程度足够高,不需要调用 readClass()。
// 直接跳过这个镜像,处理下一个。
continue;
}
// 从当前镜像的 __objc_classlist 中取出类列表。
//
// classlist 指向该镜像中定义的所有 Objective-C 类。
// count 会被设置为类的数量。
classref_t const *classlist = info.hi->classlist(&count);
// 判断当前镜像是否是 bundle。
//
// bundle 通常是运行时动态加载的模块,例如插件、测试 bundle 等。
// readClass() 会根据这个信息采用不同的处理策略。
bool headerIsBundle = info.hi->isBundle();
// 判断当前镜像的 Objective-C 引用是否已经被 dyld 预优化。
//
// 如果为 true,说明 selector refs、class refs、super refs 等
// 可能已经被 dyld 提前修正过。
bool headerIsPreoptimized = info.dyldObjCRefsOptimized();
// 遍历当前镜像中的每一个类。
for (i = 0; i < count; i++) {
// 取出类列表中的原始 Class 指针。
//
// 这个 cls 来自 Mach-O 的 __objc_classlist,
// 是编译器/链接器生成的类结构。
Class cls = (Class)classlist[i];
// 读取并注册这个类。
//
// readClass() 会把 Mach-O 中的类结构纳入 Runtime 管理,
// 包括类名登记、父类关系处理、metaclass 处理、
// future class 解析、状态标记等。
//
// 普通情况下,返回值 newCls 等于 cls。
// 特殊情况下,例如解析 future class,返回值可能是另一个 Class。
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 如果 readClass() 返回的类和原始 cls 不同,并且 newCls 非空,
// 说明这个类没有被删除,而是被"移动"到了另一个 Class 结构上。
//
// 当前 Runtime 中,这种情况主要发生在:
// 新加载的真实类解析了之前创建的 future class。
if (newCls != cls && newCls) {
// 类被移动但没有被删除。
// 当前主要场景是 newCls 解析了一个 future class。
//
// 这种类后面需要立即 realize,不能再懒加载。
// 因此先把它记录到 resolvedFutureClasses 数组中。
// 扩容 resolvedFutureClasses 数组,
// 新大小为 resolvedFutureClassCount + 1 个 Class。
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
// 把解析后的 future class 保存起来,
// 并递增 resolvedFutureClassCount。
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
4.修复重映射一些没有被镜像文件加载进来的类
objc
if (!noClassesRemapped()) {
for (auto info : infos) {
Class *classrefs = info.hi->classrefs(&count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);//上面说过的future Class的例子,就需要remap
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = info.hi->superrefs(&count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
如果runtime发现有Class指针发生过重映射,就遍历所有镜像中的类引用和父类引用,将旧的Class指针修正成新的Class指针
- _getObjc2ClassRefs
是获取Mach-O中的静态段、__objc_classrefs即类的引用 - _getObjc2SuperRefs
是获取Mach-O中的静态段__objc_superrefs即父类的引用
remap必须发生在类真正realize之前,而绝大多数还没realize的类正好属于懒加载类
5.修复一些消息
objc
for (auto info : infos) {
message_ref_t *refs = info.hi->messagerefs(&count);//从镜像文件中取出镜像参考表
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, info.hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
遍历当前加载的所有 Mach-O 镜像,找到其中的旧式 Objective-C 消息发送引用 messagerefs,并逐个调用 fixupMessageRef() 进行修复,使这些旧的消息发送调用点能够在当前 Runtime 下正常工作。
6.读取协议列表
objc
for (auto info : infos) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();//获取全局协议表,懒加载初始化
bool isPreoptimized = info.dyldObjCRefsOptimized();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
// definition
if (launchTime && isPreoptimized) {//如果在启动阶段并且已经预优化了,就不用再次处理。
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
info.hi->fname());
}
continue;
}
bool isBundle = info.hi->isBundle();
protocol_t * const *protolist = info.hi->protocollist(&count);//获取镜像文件中的静态段协议列表,即从编译器中读取并初始化protocal
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
遍历当前加载的 Mach-O 镜像,读取其中定义的 Objective-C 协议 protocol,并把这些协议注册/合并到 Runtime 的全局协议表中;但在启动阶段,如果某个镜像已经被 dyld 预优化,则可以跳过读取,以减少启动开销。
7.修复没有被加载的协议
objc
for (auto info : infos) {
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
if (launchTime && info.hi->isPreoptimized())
continue;
protocol_t **protolist = info.hi->protocolrefs(&count);//镜像文件中的协议引用段
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
为什么被dyld预优化过的
镜像可以跳过?启动阶段,预优化镜像里的协议引用已经被 dyld 修好了,指向 shared cache 里的协议定义,所以 Runtime 不需要再检查。
objc
static size_t UnfixedProtocolReferences;//计数修正数量
static void remapProtocolRef(protocol_t **protoref)
{
lockdebug::assert_locked(&runtimeLock);
protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
if (*protoref != newproto) {
*protoref = newproto;
UnfixedProtocolReferences++;
}
}
8.分类处理
objc
if (didInitialAttachCategories) {
for (auto info : infos) {
load_categories_nolock(info.hi);
}
}
这里主要是处理分类,需要在分类初始化并将数据加载到类之后才执行,对与运行时出现的分类,将分类的发现推迟到_dyld_objc_notify_register的调用完成后的第一个load_images调用为止
9.类的加载处理
objc
// 遍历当前这一批被 dyld 加载并交给 ObjC Runtime 处理的镜像。
// 每个 info 表示一个 Mach-O image。
for (auto info : infos) {
// 取出当前镜像中的 non-lazy class list。
//
// nlclslist 对应 Mach-O 中的 __objc_nlclslist。
// 里面存放的是必须在镜像加载阶段立即 realize 的类。
//
// 常见情况:实现了 +load 方法的类。
classref_t const *classlist = info.hi->nlclslist(&count);
// 遍历当前镜像中的所有 non-lazy class。
for (i = 0; i < count; i++) {
// classlist / nlclslist 本身可能没有被直接 remap。
//
// 如果前面 readClass() 阶段发生了 Class remap,
// 例如 oldCls -> newCls,
// 那么这里必须先通过 remapClass() 取得 Runtime 当前认可的 Class。
Class cls = remapClass(classlist[i]);
// 如果 remap 后没有有效 Class,
// 说明该类无效、缺失或已被 Runtime 忽略,直接跳过。
if (!cls) continue;
// 将该类加入 Runtime 的类表。
//
// 这样 objc_getClass()、NSClassFromString() 等运行时查找
// 能够找到这个类。
//
// non-lazy class 后续马上可能被 realize 和调用 +load,
// 所以需要确保它已经在类表中可见。
addClassTableEntry(cls);
// 如果这是一个 Swift stable ABI 类,
// 需要额外检查它是否带 Swift metadata initializer。
if (cls->isSwiftStable()) {
// 带 Swift metadata initializer 的 Swift 类
// 不能作为 ObjC non-lazy class 在这里直接 realize。
//
// 因为这种类的元数据初始化需要 Swift runtime 参与,
// 而 realizeClassWithoutSwift() 不负责执行 Swift metadata initializer。
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// FIXME:
// 理论上还应该禁止 relocatable Swift classes。
//
// 但不能禁止所有 Swift 类,
// 因为 Swift 标准库中存在一些可以走该路径的特殊类,
// 例如 Swift.__EmptyArrayStorage。
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 立即 realize 这个 non-lazy class。
//
// readClass() 只是把类读入 Runtime;
// realizeClassWithoutSwift() 会真正展开类结构:
// - 建立 superclass / metaclass 关系
// - 初始化 class_rw_t
// - 整理 method / property / protocol list
// - 初始化 cache
// - 标记类为 realized
//
// 完成后,该类可以参与消息发送、方法查找和 +load 调用。
realizeClassWithoutSwift(cls, nil);
}
}
这段代码负责将所有的非懒加载类立即加入runtime类表并完成realize,使他们在+load或者后续使用前已经与欧完整的运行时结构。(如果一个类实现了load方法,那么它通常会进入非懒加载类,以为+load要在镜像加载时由runtime主动调用)
10.没有被处理的类,优化那些被侵犯的类
objc
// 如果前面 readClass() 阶段解析过 future class,
// resolvedFutureClasses 会保存这些已经解析成功的 future Class。
if (resolvedFutureClasses) {
// 遍历所有本轮解析成功的 future class。
for (i = 0; i < resolvedFutureClassCount; i++) {
// 取出一个已经解析成功的 future class。
// 这个 cls 通常不是 Mach-O classlist 里的原始 Class,
// 而是 readClass() 返回的 newCls。
Class cls = resolvedFutureClasses[i];
// Swift stable ABI class 不允许使用 ObjC future class 机制。
// 因为 Swift class 的元数据初始化由 Swift runtime 管理,
// 不能简单用 Objective-C Runtime 的 future Class 占位承接。
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
// 立即 realize 这个 future class。
//
// 普通 lazy class 可以等第一次使用时再 realize;
// 但 future class 的 Class 指针可能已经提前暴露给外部代码,
// 所以真实类解析完成后必须立即完成 Runtime 展开。
//
// realize 后,该类会完成 superclass/metaclass 连接、
// class_rw_t 初始化、方法/协议/属性列表整理、cache 初始化等。
realizeClassWithoutSwift(cls, nil);
// future class 在解析前可能被 Runtime 保守地要求实例使用 raw isa。
//
// 现在真实类已经解析并 realize 完成,
// 可以取消"强制 raw isa"的限制,
// 让该类及相关子类按正常规则使用 isa 策略。
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
// 释放临时数组。
//
// 注意:这里只释放 resolvedFutureClasses 数组本身,
// 不释放数组中的 Class 对象。
free(resolvedFutureClasses);
}
readClass
读取类,在未调用该方法之前,cls只是一个地址,执行该方法之后,cls是类的名称。这个函数完成的是类发现阶段的注册、future class 解析、弱链接父类剔除、预优化路径区分以及 bundle 来源标记 ,但它还不是完整的类展开,真正的方法列表、父类链、缓存等运行时结构初始化会在后续 realizeClass...阶段完成
realizeClassWithoutSwift
实现将类的data数据加载到内存中,主要有以下几步:
- 读取data数据,并设置ro、rw:这一步读取class的data数据,并将其强制转换为ro,并初始化rw,将ro中的数据拷贝一份到rw的ro中。(rw具有动态性,但是实际上之后10%左右的类真正更改了他们的方法,所以衍生出了rwe即类的额外信息,对那些需要额外信息的类分配一份rwe并将其放入类中供其使用。)
- 递归调用realizeClassWithoutSwift完善继承链:递归调用 realizeClassWithoutSwift 设置父类、元类。设置父类和元类的isa指向 。通过addSubclass 和 addRootClass 设置父子的双向链表指向关系 ,即父类中可以找到子类,子类中可以找到父类。在realize~递归调用时,isa找到根元类后,根元类的isa指向自己,并不会返回nil,所以需要有下面几个终止条件:1.如果类不存在返回nil。2.如果类已经实现返回nil
- 通过methodizeClass方法化类
methodizeClass
该方法会提取类本身的方法、属性、协议,存入rw的动态列表,从运行时获取的分类的方法、属性、协议合并到rw对应表中,确保rw包含所有可访问的成员,供运行时的动态查询
方法列表加入rwe的逻辑
- 取出类自己在编译器的方法列表(只读)
- 对方法列表做运行时预处理
- 将方法列表挂入class_rw_ext_t 的 methods 中
attachToClass
将分类添加到主类中。
attachCategories

注意点
- 逆序存储正序附加:通过倒序填充缓冲区,正序附加,确保后加载的分类内容优先生效。
- 分批处理:每满64个分类处理一次,平衡性能与内存占用。
- 方法覆盖:分类方法插入到类方法列表头部,实现"后编译的分类覆盖先编译的类或分类方法"。

分类的加载时机
non-lazy category 的触发条件可能只是某一个 category 需要在加载期处理,但 Runtime 不能把同一镜像内的 category 拆开处理;一旦拆开,方法覆盖顺序、协议/属性附加顺序、+load 收集顺序都可能不一致。因此只要一个 category 是 non-lazy,该镜像内所有 category 都会被整体作为 non-lazy categories 处理。
load_images
objc
void
load_images(const struct _dyld_objc_notify_mapped_info* info)
{
if (slowpath(PrintImages)) {
_objc_inform("IMAGES: calling +load methods in %s\n", info->path ? info->path : "<null>");
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)info->mh, info->sectionLocationMetadata)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Load all pending categories if they haven't been loaded yet, and discover
// load methods.
{
mutex_locker_t lock2(runtimeLock);
loadAllCategoriesIfNeeded();
prepare_load_methods((const headerType *)info->mh, info->sectionLocationMetadata);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
加载所有分类,其实是在分类中遍历,检查分类的主类是否已经实现,如果已经实现就将分类合并,否在就放到_unattachedCategories中,这一步就是对之前提到的对懒加载分类的处理,当懒加载类实现之后,就将分类合并上去
prepare_load_methods
objc
// 准备当前 Mach-O 镜像中的 +load 方法。
// 注意:这里只是收集并加入待调用队列,不是真正调用 +load。
void prepare_load_methods(const headerType *mhdr,
const _dyld_section_location_info_t info)
{
size_t count, i;
// 要求调用方已经持有 runtimeLock。
// 因为这里会访问类状态、remap 表和 +load 待调用列表。
lockdebug::assert_locked(&runtimeLock);
// 读取当前镜像中的 non-lazy class list。
//
// 对应 Mach-O 中的 __objc_nlclslist。
// 这里通常存放实现了 +load 的类。
classref_t const *classlist =
getSectionData<classref_t>(
mhdr,
info,
_dyld_section_location_data_non_lazy_class_list,
&count);
// 遍历所有 non-lazy class。
for (i = 0; i < count; i++) {
// classlist[i] 可能是旧 Class 指针。
// 前面的 readClass() 可能发生过 class remap:
// oldCls -> newCls 或 oldCls -> nil。
//
// 所以这里先 remapClass(),拿到 Runtime 当前认可的 Class。
//
// schedule_class_load() 会把类的 +load 安排到待调用列表中,
// 并保证父类 +load 先于子类 +load。
schedule_class_load(remapClass(classlist[i]));
}
// 读取当前镜像中的 non-lazy category list。
//
// 对应 Mach-O 中的 __objc_nlcatlist。
// 这里通常存放实现了 +load 的分类。
category_t * const *categorylist =
getSectionData<category_t *>(
mhdr,
info,
_dyld_section_location_data_non_lazy_category_list,
&count);
// 遍历所有 non-lazy category。
for (i = 0; i < count; i++) {
// 取出一个 category 元数据。
category_t *cat = categorylist[i];
// category 的目标类。
// 同样需要 remapClass(),避免使用旧 Class 指针。
Class cls = remapClass(cat->cls);
// 如果目标类无效,直接跳过。
// category 附加到一个被忽略的 weak-linked class 上。
if (!cls) continue; // category for ignored weak-linked class
// Swift stable ABI class 不允许通过 ObjC category / extension
// 定义 +load。
// 如果发现 Swift 类上的 category 有 +load,Runtime 直接终止。
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
// 在调度 category 的 +load 之前,
// 必须确保 category 的目标类已经完成 realize。
// realize 后,目标类的 superclass、metaclass、方法列表、
// 属性、协议等 Runtime 结构才是稳定的。
realizeClassWithoutSwift(cls, nil);
// category 的 +load 是类方法层面的行为,
// 因此目标类的元类也必须已经 realize。
ASSERT(cls->ISA()->isRealized());
// 将该 category 加入 Runtime 的 loadable category 列表。
//
// 注意:这里只是加入待调用列表,不立即调用。
// 后续 call_load_methods() 会统一调用 category 的 +load。
add_category_to_loadable_list(cat);
}
}
这个方法会将类以及其分类的load方法都放到数组中
schedule_class_load
objc
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->getSuperclass());
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
根据类的继承链递归调用获取load方法,确保父类的load方法优先加载
add_class_to_loadable_list
objc
void add_class_to_loadable_list(Class cls)
{
IMP method;
lockdebug::assert_locked(&loadMethodLock);
method = cls->getLoadMethod();//只判断当前类
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
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++;
}
通过这个方法将load方法与cls类名一起添加到loadable_classes表中
getLoadMethod
objc
objc_class::getLoadMethod()
{
lockdebug::assert_locked(&runtimeLock);
ASSERT(isRealized());
ASSERT(ISA()->isRealized());
ASSERT(!isMetaClass());
ASSERT(ISA()->isMetaClass());
auto &baseMethods = ISA()->data()->ro()->baseMethods;
if (auto *list = baseMethods.dyn_cast<method_list_t *>()) {
return _getLoadMethod(list);
} else if (auto *listList = baseMethods.dyn_cast<relative_list_list_t<method_list_t> *>()) {
// A load method will always be in the last list, since it's in the
// class itself rather than in a category, and all other lists are
// category lists.
return _getLoadMethod(listList->lastList());
}
return nullptr;
}
从元类中查找这个类自己实现的load方法,并返回它的IMP,如果没有实现load,返回nulltpr
add_category_to_loadable_list
objc
void add_category_to_loadable_list(Category cat)
{
IMP method;
lockdebug::assert_locked(&loadMethodLock);
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++;
}
同类的流程,查找分类中的load方法,将分类中load方法加入表loadable_categories中
call_load_methods
objc
void call_load_methods(void)
{
static bool loading = NO;//避免内层递归调用load,导致触发新的镜像加载
bool more_categories;
lockdebug::assert_locked(&loadMethodLock);
// 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;
}
执行时始终优先清空 class +load 队列,再调用一轮 category +load,如果过程中又产生新的 class 或 category +load,则继续循环,直到没有待执行项。它保证了 +load 的核心顺序约束:类先于分类、父类先于子类、动态加载中新产生的类 +load 仍优先于后续分类 +load。
initialize分析

objc
static Class
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
lockdebug::assert_locked(&runtimeLock);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);//会暂时释放锁
// runtimeLock may have been dropped but is now locked again
}
if (slowpath(initialize && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
return cls;
}
objc
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
lockdebug::assert_locked(&lock);
ASSERT(cls->isRealized());
if (cls->isInitialized()) {
if (!leaveLocked) lock.unlock();
return cls;
}
// Find the non-meta class for cls, if it is not already one.
// The +initialize message is sent to the non-meta class object.
Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);//永远发送给普通类对象,不是元类
// Realize the non-meta class if necessary.
//如果传进来的是元类,前面只判断了元类是否realized,不能确保类对象是否realized
if (nonmeta->isRealized()) {
// nonmeta is cls, which was already realized
// OR nonmeta is distinct, but is already realized
// - nothing else to do
lock.unlock();
} else {
nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
// runtimeLock is now unlocked
// fixme Swift can't relocate the class today,
// but someday it will:
cls = object_getClass(nonmeta);
}
// runtimeLock is now unlocked, for +initialize dispatch
ASSERT(nonmeta->isRealized());
initializeNonMetaClass(nonmeta);
if (leaveLocked) runtimeLock.lock();
return cls;
}
initializeNonMetaClass
objc
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
Class supercls = cls->getSuperclass();
if (supercls && !supercls->isInitialized()) {//子类初始化前需要确保父类初始化完成
initializeNonMetaClass(supercls);
}
// Acquire the initialization lock for this class.
lockClass(cls);//获取当前类的初始化锁,解决多线程竞争
// Now that it's acquired, there are three possibilities:
// 1. Initialized. We waited, now it's done, return.
// 2. Initializing.
// A. This thread is already initializing the class and we
// reacquired the recursive lock.
// B. We're in the child of a fork, another thread was initializing the
// class in the parent process, and no longer exists in the child.
// 3. Neither. This thread won the race to initialize cls, do it.
// Case 1, we waited.
if (cls->isInitialized()) {
unlockClass(cls);
return;
}
// Case 2, we reentered initialization.
if (cls->isInitializing()) {
// Case 2A, we're not in a fork child, or we are but the class is
// initializing on this thread, so we can just return.
if (!MultithreadedForkChild || _thisThreadIsInitializingClass(cls)) {//当前线程自己重入初始化,通常发生在+initialize 内部又给本类发消息。
unlockClass(cls);
return;
} else {//进程 fork 安全处理。子进程看到类处于 initializing,但那个初始化线程已经消失。所以 Runtime 需要特殊接管,执行 fork-child 初始化路径。
// Case 2B, we're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
// The lock for this class has been dropped, so reacquire it here.
lockClass(cls);
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
// Case 3, we won the race. Set CLS_INITIALIZING and gather will-initialize
// functions.
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
mutex_locker_t lock(classInitLock);
cls->setInitializing();//标记
localWillInitializeFuncs.initFrom(willInitializeFuncs);//Runtime 可能注册了一些"类即将初始化"的回调函数。这里拷贝一份到本地,避免后面执行回调时长期持有全局锁
}
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);//记录当前线程正在初始化这个类,避免重入
if (MultithreadedForkChild) {//fork进来的需要走特殊路径,保证安全性
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
for (auto callback : localWillInitializeFuncs)
callback.f(callback.context, cls);//执行回调
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
@try
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
}
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
objc_thread_self(), cls->nameForLogging());
}
@throw;
}
@finally
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);//不管是否抛出异常,都标记完成
}
}
- intialize在类或者其子类的第一个方法被调用之前调用
- 父类的initalize比子类先执行
- 当子类未实现initialize时会调用父类的initalize方法。子类实现时会覆盖父类的initialize方法
- 有多个分类覆盖类的initialize时,只会执行最后被加载到内存的分类的initialize方法