前言
在dyld
加载过程中,我们知道会调用_objc_init
方法,那么在_objc_init
方法中究竟做了什么呢?我们来探究下。
_objc_init方法
_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?<br /> environ_init();<br /> tls_init();<br /> static_init();<br /> runtime_init();<br /> exception_init();<br /> cache_init();<br /> _imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
if OBJC2
didCallDyldNotifyRegister = true;
#endif
}
从_objc_init
实现中我们分析下该方法主要做了什么
environ_init()
该方法主要是读取运行时的环境变量,我们可以通过设置DYLD_PRINT_STATISTICS = YES
来打印APP启动到main()函数之前的时长,进而可以进行APP启动优化。具体的environ_init()简介可参考博客iOS-底层原理 16:dyld与objc的关联中有关nviron_init()
部分的介绍
tls_init()
主要用于关于线程key的绑定,比如每线程数据的析构函数。
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++静态构造函数
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
initsi;
}
}
runtime_init()
主要是运行时的初始化,主要分为两部分:分类初始化
和类的表初始化
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}
复制代码
exception_init()
初始化libobjc异常处理
/*
exception_init
Initialize libobjc’s exception handling system.
Called by map_images().
*/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
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) {<br /> count++;<br /> }
kr = task_restartable_ranges_register(mach_task_self(),<br /> objc_restartableRanges, count);<br /> if (kr == KERN_SUCCESS) return;<br /> _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",<br /> kr, mach_error_string(kr));<br />#endif // HAVE_TASK_RESTARTABLE_RANGES<br />}
_imp_implementationWithBlock_init()
主要用来启动机制回调
/// 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关联
_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);
}
从上文正中我们可以看出
mapped
即map_images
init
即load_images
unmapped
即unmap_image
map_images()函数分析
/*
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
函数中我们发现map_images_nolock函数
是重点,我们进入map_images_nolock
函数
map_images_nolock
我们查看代码实现
从截图中我们可以看出_read_images
是我们要重点研究的方法
_read_images函数分析
是否是第一次加载
修复预编译时**@selector**
的错乱问题
错误类处理,通过**readClass**
读取出来类的信息
重新设置映射镜像
消息处理
类中如果有协议,读取协议
映射协议
加载分类
注意
在分类处理中主要是通过load_categories_nolock
处理,我们进入load_categories_nolock
函数中
**load_categories_nolock**
函数
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;<br /> auto processCatlist = [&](category_t * const *catlist) {<br /> for (unsigned i = 0; i < count; i++) {<br /> category_t *cat = catlist[i];<br /> Class cls = remapClass(cat->cls);<br /> locstamped_category_t lc{cat, hi};
if (!cls) {<br /> // Category's target class is missing (probably weak-linked).<br /> // Ignore the category.<br /> if (PrintConnecting) {<br /> _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "<br /> "missing weak-linked target class",<br /> cat->name, cat);<br /> }<br /> continue;<br /> }
// Process this category.<br /> if (cls->isStubClass()) {<br /> // Stub classes are never realized. Stub classes<br /> // don't know their metaclass until they're<br /> // initialized, so we have to add categories with<br /> // class methods or properties to the stub itself.<br /> // methodizeClass() will find them and add them to<br /> // the metaclass as appropriate.<br /> if (cat->instanceMethods ||<br /> cat->protocols ||<br /> cat->instanceProperties ||<br /> cat->classMethods ||<br /> cat->protocols ||<br /> (hasClassProperties && cat->_classProperties))<br /> {<br /> objc::unattachedCategories.addForClass(lc, cls);<br /> }<br /> } else {<br /> // First, register the category with its target class.<br /> // Then, rebuild the class's method lists (etc) if<br /> // the class is realized.<br /> if (cat->instanceMethods || cat->protocols<br /> || cat->instanceProperties)<br /> {<br /> if (cls->isRealized()) {<br /> attachCategories(cls, &lc, 1, ATTACH_EXISTING);<br /> } else {<br /> objc::unattachedCategories.addForClass(lc, cls);<br /> }<br /> }
if (cat->classMethods || cat->protocols<br /> || (hasClassProperties && cat->_classProperties))<br /> {<br /> if (cls->ISA()->isRealized()) {<br /> attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);<br /> } else {<br /> objc::unattachedCategories.addForClass(lc, cls->ISA());<br /> }<br /> }<br /> }<br /> }<br /> };
processCatlist(_getObjc2CategoryList(hi, &count));<br /> processCatlist(_getObjc2CategoryList2(hi, &count));<br />}
从load_categories_nolock
函数实现中,我们可以看到该函数将类
、实例方法
、协议
、属性
、类方法
等再次链接了一次。
非懒加载类处理
处理没有使用的类
dyld与objc关联总结
dyld_start
调用_objc_init
来初始化,_objc_init
中通过dyld
调用_dyld_objc_notify_register
函数,传入map_images
跟load_images
这两个参数来处理map_images
通过map_images_nolock
函数调用_read_images
函数- 在
_read_images
函数中处理类信息、属性、协议、分类等
- 当一切准备妥当,则再次返回
dyld_start
中,此时dyld
跟objc
关联了起来
资料推荐
如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。