[OC学习笔记]启动流程(objc部分)
Posted Billy Miracle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OC学习笔记]启动流程(objc部分)相关的知识,希望对你有一定的参考价值。
先回顾下这张图,回顾下整体流程。现在分析下在此流程中objc4源码(818.2)的处理逻辑。
_objc_init解析
我们在上图可以看出,dyld在main
函数之前(pre-main)会间接调用到objc
的_objc_init
,其中使用_dyld_objc_notify_register
注册了3个方法,但在这之前还做了一些初始化的操作。
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
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();
//绑定线程析构函数
tls_init();
//静态构造函数
static_init();
//runtime准备,创建2张表
runtime_init();
//异常初始化
exception_init();
#if __OBJC2__
//缓存
cache_t::init();
#endif
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
environ_init
其中environ_init
是读取环境变量(environment variables
)的一些配置信息,environment variables
在Product
->Scheme
->Edit Scheme
->Run
->Argments
->Environment Variables
中配置。设置相关信息可以打印一些信息。可以设置OBJC_HELP
,启动程序后会打印所有可以设置的信息已经含义解释。
输出:
objc[2763]: OBJC_HELP: describe available environment variables//描述可用的环境变量
objc[2763]: OBJC_HELP is set
objc[2763]: OBJC_PRINT_OPTIONS: list which options are set//列出设置的选项
objc[2763]: OBJC_PRINT_OPTIONS is set
objc[2763]: OBJC_PRINT_IMAGES: log image and library names as they are loaded//在加载时输出image和library名称
objc[2763]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[2763]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods//打印Class及Category的+(void)load 方法的调用信息
objc[2763]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods//打印Class的+(void)initialize的调用信息
objc[2763]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod://打印通过+resolveClassMethod:和+resolveInstanceMethod:生成的类方法
objc[2763]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup//打印Class及Category的设置过程
objc[2763]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup//打印Protocol的设置过程
objc[2763]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars//打印lar的设置过程
objc[2763]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables//打印vtable的设置过程
objc[2763]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods//打印vtable被覆盖的方法
objc[2763]: OBJC_PRINT_CACHE_SETUP: log processing of method caches//打印方法缓存的设置过程
objc[2763]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging//打印从CFType无缝转换到NSObject将要使用的类(如CFArrayRef到NSArray *)
objc[2763]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache//打印dyld共享缓存优化前的问候语
objc[2763]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables//打印类实例中的C++对象的构造与析构调用
objc[2763]: OBJC_PRINT_EXCEPTIONS: log exception handling//打印异常处理
objc[2763]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()//打印所有异常抛出时的回溯
objc[2763]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers//打印alt操作异常处理
objc[2763]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations//打印被Category替换的方法
objc[2763]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions//打印所有过时的方法调用
objc[2763]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools//打印autoreleasepool高水位警告
objc[2763]: OBJC_PRINT_CUSTOM_CORE: log classes with custom core methods//打印具有自定义核心方法的类
objc[2763]: OBJC_PRINT_CUSTOM_RR: log classes with custom retain/release methods//打印含有未优化的自定义retain/release方法的类
objc[2763]: OBJC_PRINT_CUSTOM_AWZ: log classes with custom allocWithZone methods//打印含有未优化的自定义allocWithzone方法的类
objc[2763]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields//打印需要访问原始isa指针的类
objc[2763]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded//卸载有不良行为的Bundle时打印警告
objc[2763]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses//当子类可能被对父类的修改破坏时打印警告
objc[2763]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization//警告@synchronized(nil)调用,这种情况不会加锁
objc[2763]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars//打印突发地重新布置non-fragileivars的行为
objc[2763]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use//记录更多的alt操作错误信息
objc[2763]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak//警告没有pool的情況下使用autorelease,可能内存泄漏
objc[2763]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools//当自动释放池无序弹出时停止,并允许堆调试器跟踪自动释放池
objc[2763]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present//当出现类重名时停机
objc[2763]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing//通过退出而不是崩溃来停止进程
objc[2763]: OBJC_DEBUG_POOL_DEPTH: log fault when at least a set number of autorelease pages has been allocated//分配了至少设定数量的自动发布页时打印错误
objc[2763]: OBJC_DEBUG_SCRIBBLE_CACHES: scribble the IMPs in freed method caches//在释放的方法缓存中将IMP改乱
objc[2763]: OBJC_DEBUG_SCAN_WEAK_TABLES: scan the weak references table continuously in the background - set OBJC_DEBUG_SCAN_WEAK_TABLES_INTERVAL_NANOSECONDS to set scanning interval (default 1000000)
objc[2763]: OBJC_DISABLE_VTABLES: disable vtable dispatch//关闭vtable分发
objc[2763]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache//关闭avld共享缓存优化前的问候语
objc[2763]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.//关闭NSNumber等的tagged pointer优化
objc[2763]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[2763]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields//关闭non-pointer isa字段的访问
objc[2763]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[2763]: OBJC_DISABLE_FAULTS: disable os faults
objc[2763]: OBJC_DISABLE_PREOPTIMIZED_CACHES: disable preoptimized caches
objc[2763]: OBJC_DISABLE_AUTORELEASE_COALESCING: disable coalescing of autorelease pool pointers
objc[2763]: OBJC_DISABLE_AUTORELEASE_COALESCING_LRU: disable coalescing of autorelease pool pointers using look back N strategy
除此之外,environ_init
里面还对其他工程配置进行读取,比如NSZombiesEnabled
(僵尸对象检测)。
tls_init
接下来是 tls_init
,tls
是Thread Local Store
的缩写,线程局部存储主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护。
/***********************************************************************
* tls_init
* 关联线程析构函数
* 当线程销毁,调用以上方法
**********************************************************************/
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
这里其实是给线程添加了析构函数,线程销毁,会调用_objc_pthread_destroyspecific
方法。
接下来简单介绍下TLS:我们知道在一个进程中,所有线程是共享同一个地址空间的。所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线程对其进行了修改,也就会影响到其他所有的线程。不过我们可能并不希望这样,所以更多的推荐用基于堆栈的自动变量或函数参数来访问数据,因为基于堆栈的变量总是和特定的线程相联系的。
不过如果某些时候,我们就是需要依赖全局变量或者静态变量,那有没有办法保证在多线程程序中能访问而不互相影响呢?答案是有的。操作系统帮我们提供了这个功能——TLS线程本地存储。TLS的作用是能将数据和执行的特定的线程联系起来。
实现TLS有两种方法:静态TLS和动态TLS。
更多内容可以参考:博客、博客。
static_init
接下来是static_init
:找到objc
库中的所有初始化方法,遍历调用,注意:objc
库中的静态构造函数早于load
方法,load
方法早于我们自己的静态构造函数。
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors,
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
size_t count;
//获取objc库里面所有的静态构造函数
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
接下来是runtime_init
:
void runtime_init(void)
//分类加载表
objc::unattachedCategories.init(32);
//类的加载表
objc::allocatedClasses.init();
这里面其实是初始化两张表,以备后边加载类使用,这里可以留意一下这两张表unattachedCategories
和allocatedClasses
,后面会再次提及到。
其他
其他的初始化,由于不影响整个流程,所以在这一小节一带而过。
exception_init
:初始化异常捕捉相关
cache_t::init
:初始化缓存相关
_imp_implementationWithBlock_init
: MacOS中,让dyld
去加载libobjc-trampolines.dylib这个库。
map_images解析
上一篇文章,我们追踪到了load_images
的调用时机,这次我们直接来到他调用的地方,先分析其传入的参数。
三个参数:
参数 | 含义 |
---|---|
count | images的数量 |
paths[] | 库的路径(数组) |
mhdrs[] | 库对应的mach-header 信息(数组) |
再看map_image
的源码:
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
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_nolock
函数。下面看一下map_images_nolock
:
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
// 是否是第一次加载
static bool firstTime = YES;
// hList 是统计 mhdrs 中的每个 mach_header 对应的 header_info
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();
// 如果添加OBJC_PRINT_IMAGES环境,打印镜像数量
// 如:objc[10503]: IMAGES: processing 296 newly-mapped images...
if (PrintImages)
_objc_inform("IMAGES: processing %u newly-mapped images...\\n", mhCount);
// Find all images with Objective-C metadata.
// 计算 class 的数量。根据总数调整各种表格的大小。
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
uint32_t i = mhCount;
while (i--)
// 取得指定 image 的 header 指针
const headerType *mhdr = (const headerType *)mhdrs[i];
// 以 mdr 构建其 header_info,并添加到全局的 header 列表中(是一个链表,看源码到现在还是第一次看到链表的使用)
// 且通过 GETSECT(_getObjc2ClassList, classref_t const, "__objc_classlist"); 读取 __objc_classlist 区中的 class 数量添加到 totalClasses 中,
// 以及未从 dyld shared cache 中找到 mhdr 的 header_info 时,添加 class 的数量到 unoptimizedTotalClasses 中
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
// 这里有两种情况下 hi 为空:
// 1. mhdr 的 magic 不是既定的 MH_MAGIC、MH_MAGIC_64、MH_CIGAM、MH_CIGAM_64 中的任何一个
// 2. 从 dyld shared cache 中找到了 mhdr 的 header_info,并且 isLoaded 为 true()
if (!hi)
// no objc data in this entry
continue;
//如果是可执行文件(我们的代码生成的)MH_EXECUTE就是我们主工程的代码
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;
// 获取 __objc_selrefs 区中的 SEL 的数量
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
// 获取 __objc_msgrefs 区中的 message 数量
_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
//把hi存储起来
hList[hCount++] = hi;
if (PrintImages)
// 打印 image 信息
_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.
// 执行 one-time runtime 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.
// 如果可执行文件不包含 Objective-C 代码但稍后动态加载 Objective-C,则该可执行文件可能不会出现在此 infoList 中。
if (firstTime)
// 初始化函数注册表
sel_init(selrefCount);
// 这里的 arr_init 函数超重要,可看到它内部做了三件事
// 1.自动释放池 AutoreleasePoolPage的初始化
// 2.SideTablesMap初始化
// 3.全局关联对象表的初始化
arr_init();
/*
void arr_init(void)
AutoreleasePoolPage::init();
SideTablesMap.init();
_objc_associations_init();
if (DebugScanWeakTables)
startWeakTableScan();
*/
#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
// 这一段是在较低版本下 DYLD_MACOSX_VERSION_10_13 之前的版本中禁用 +initialize fork safety,大致看看即可
#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
//⚠️⚠️关键
// 以 header_info *hList[mhCount] 数组中收集到的 images 的 header_info 为参,直接进行 image 的读取
if (hCount > 0)
// 读取映射
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
// 把开始时初始化的静态局部变量 firstTime 置为 NO
firstTime = NO;
// _read_images 看完再看下面的 loadImageFuncs 函数
// Call image load funcs after everything is set up.
// 一切设置完毕后调用 image 加载函数。
for (auto func : loadImageFuncs)
for (uint32_t i = 0; i < mhCount; i++)
func(mhdrs[i]);
其中最重要的就是_read_images
函数的调用,map_images_nolock
上半部分就是对const struct mach_header * const mhdrs[]
参数的处理,把数组中的mach_header
转换为header_info
并存在header_info *hList[mhCount]
数组中,并统计totalClasses
和unoptimizedTotalClasses
的数量,然后调用_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses)
函数。
下面先看_read_images
的源码,发现非常长,下面根据顺序给出几段主要的代码:
准备工作
/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked
* list beginning with headerList.
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
*
* 对以 headerList 开头的链接列表中的标头执行初始处理。
* 呼叫者: map_images_nolock
* 锁定:运行时锁定由map_images获取
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil[OC学习笔记]objc_msgSend:方法快速查找