-
_objc_init 源码解析
先看源码主要是一些列东西的初始化:
environ_init
读取影响运⾏时的环境变量。如果需要,还可以打印环境变量帮助。export OBJC_HELP = 1 源码:冲源码中发现可以通过配置
OBJC_HELP和OBJC_PRINT_OPTIONS两个环境变量来打印所有环境变量。配置环境变量的方法target -- Edit Scheme -- Run --Arguments -- Environment Variables
第二种打印环境变量的方式是通过终端指令export OBJC_hrlp = 1tls_init
关于线程 key 的绑定,比如每线程数据的析构函数。static_init
这里会运行 C++ 的静态构造函数,在 dyld 调用我们的静态构造函数之前,libc 会调用 _objc_init,所以这里我们必须自己来做,并且这里只会初始化系统内置的 C++ 静态构造函数,我们自己代码里面写的并不会在这里初始化。所以系统的c++函数会先于自定义的c++函数执行runtime_init
主要是运行时的初始化,主要分为两部分:分类初始化、类的表初始化exception_init
初始化 libobjc 的异常处理系统发现会走到
_objc_terminate方法中去再看
uncaught_handler方法发现默认是个空实现在全局搜索
uncaught_handler发现调用objc_setUncaughtExceptionHandler可以设置
uncaught_handler方法所以应用记得crash的拦截可以通过设置uncaught_handler方法拦截也就是调用objc_setUncaughtExceptionHandler方法cache_init
缓存初始化void cache_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
该方法主要是启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载libobjc-trampolines.dylib,其源码如下/// Initialize the trampoline machinery. Normally this does nothing, as /// everything is initialized lazily, but for certain processes we eagerly load /// the trampolines dylib. 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注册,具体源码在dyld源码中void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped) { dyld::registerObjCNotifiers(mapped, init, unmapped); } //registerObjCNotifiers方法 void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped) { // record functions to call sNotifyObjCMapped = mapped; sNotifyObjCInit = init; sNotifyObjCUnmapped = unmapped; // call 'mapped' function with all images mapped so far try { notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true); } catch (const char* msg) { // ignore request to abort during registration } // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem) for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* image = *it; if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0); (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); } } }
-
dyld与objc的关联
上述源码中发现objc初始化的方法中会注册dyld,会传入
map_imagesdyld将image(镜像文件)加载进内存时,会触发该函数
load_imagesdyld初始化image会触发该函数
unmap_imagedyld将image移除时,会触发该函数
而在dyld源码中在registerObjCNotifiers方法源码中发现dyld分别将三个方法赋值给sNotifyObjCMapped = mapped; sNotifyObjCInit = init; sNotifyObjCUnmapped = unmapped;相当于
sNotifyObjCMapped = mapped = map_images
sNotifyObjCInit = init = load_images
sNotifyObjCUnmapped = unmapped = unmap_imageload_images方法的出发时机 上文中分析dyld的加载流程juejin.cn/post/691870…的时候知道load_images方法调用在dyld中其实就是sNotifyObjCInit调用sNotifyObjCInit的调用又在notifySingle函数中执行notifySingle函数的调用是在dyld::main函数中初始化镜像文件的时候调用map_images方法的出发时机 上文分析中得知map_images函数的调用在dyld中对应的就是sNotifyObjCMapped的调用,所以在dyld源码中全局搜索sNotifyObjCMapped发现是在notifyBatchPartial函数中被调用然后再全局搜索
notifyBatchPartial调用的地方发现是在
registerObjCNotifiers注册通知的时候调用的,所以冲这里也可以知道map_images函数其实在_objc_init函数中被调用,同时也验证了map_images的执行是在load_images函数之前的 最后总结dyld与objc的关联如下:
在dyld中注册回调函数,可以理解为 添加观察者
在objc中dyld注册,可以理解为发送通知
触发回调,可以理解为执行通知selector