OC底层原理11—dyld与objc的关联
本文介绍dyld与objc的关联
按照苹果的解释,dyld加载库时,libSystem会调用_objc_init
,用dyld注册镜像程序。那来看看Objc中的初始化_objc_init
做了什么
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
//读取影响运行时的环境变量,如果需要,还可以打开环境变量帮助 export OBJC_HRLP = 1
environ_init();
//关于线程key的绑定,例如线程数据的析构函数
tls_init();
//运行C++静态构造函数,在dyld调用我们的静态析构函数之前,libc会调用_objc_init(),因此我们必须自己做
static_init();
//runtime运行时环境初始化,里面主要是unattachedCategories、allocatedClasses -- 分类初始化
runtime_init();
//初始化libobjc的异常处理系统
exception_init();
//缓存条件初始化
cache_init();
//启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib
_imp_implementationWithBlock_init();
/*
_dyld_objc_notify_register -- dyld 注册的地方
- 仅供objc运行时使用
- 注册处理程序,以便在映射、取消映射 和初始化objc镜像文件时使用,dyld将使用包含objc_image_info的镜像文件数组,回调 mapped 函数
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
}
-
environ_init();
环境变量的设置,可以更好的监控程序。小到去除打印日志时的时间,还可以设置调用函数,检验哪些类是否调用了固定的函数(诸如load,initwithxxx等)就可以在
Edit Scheme
添加OBJC_PRINT_LOAD_METHODS
为YES,就可以打印出实现load方法的消息。
dyld_objc_notify_register
这是dyld
注册处理程序
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
从dyld
源码中找到该方法的解释
//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
//
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);
}
注意:仅供objc运行时使用
在objc镜像映射、未映射和初始化时调用,用来注册处理程序。
dyld将会通过一个包含objc-image-info的镜像文件的数组回调mapped函数。
那些是dylibs的镜像将自动增加引用计数,因此objc以防止它们被卸载,将不再需要
对它们调用dlopen()。 在调用_dyld_objc_notify_register()的过程中,dyld将使用已加载的objc镜像调用“mapped”函数。 在以后的任何dlopen()调用期间,dyld也将调用“mapped”函数。
当调用dyld时,dyld将调用“ init”函数初始化镜像。
objc镜像在调用任何类方法+load()的时候,也会初始化。
方法中的三个参数分别表示的含义如下:
map_images:dyld将image(镜像文件)加载进内存时,会触发该函数
load_image:dyld初始化image会触发该函数
unmap_image:dyld将image移除时,会触发该函数
image.png
- 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());
}
}
}
在objc中调用初始化时objc_init
,会调用 _dyld_objc_notify_register(&map_images, load_images, unmap_image);
,在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
实现,得到了
map_images = sNotifyObjCMapped = mapped
load_images = sNotifyObjCInit = init
unmap_image = sNotifyObjCUnmapped = unmapped