写点什么

iOS 底层原理之—dyld 与 objc 的关联

用户头像
iOSer
关注
发布于: 2020 年 10 月 17 日

前言



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?
environ_init();
tls_init();
static_init();
runtime_init();
exception_init();
cache_init();
_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++) {
inits[i]();
}
}


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) {
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()



主要用来启动机制回调



/// 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);
}


从上文正中我们可以看出



  • mappedmap_images

  • initload_images

  • unmappedunmap_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;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};

if (!cls) {
// Category's target class is missing (probably weak-linked).
// Ignore the category.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}

// Process this category.
if (cls->isStubClass()) {
// Stub classes are never realized. Stub classes
// don't know their metaclass until they're
// initialized, so we have to add categories with
// class methods or properties to the stub itself.
// methodizeClass() will find them and add them to
// the metaclass as appropriate.
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls);
}
}

if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};

processCatlist(_getObjc2CategoryList(hi, &count));
processCatlist(_getObjc2CategoryList2(hi, &count));
}


load_categories_nolock函数实现中,我们可以看到该函数将实例方法协议属性类方法等再次链接了一次。



非懒加载类处理



处理没有使用的类



dyld与objc关联总结



  • dyld_start调用_objc_init来初始化,_objc_init中通过dyld调用_dyld_objc_notify_register函数,传入map_imagesload_images这两个参数来处理

  • map_images通过map_images_nolock函数调用_read_images函数

  • _read_images函数中处理类信息、属性、协议、分类等

  • 当一切准备妥当,则再次返回dyld_start中,此时dyldobjc关联了起来



资料推荐



如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。





用户头像

iOSer

关注

微信搜索添加微信 mayday1739 进微信群哦 2020.09.12 加入

更多大厂面试资料进企鹅群1012951431

评论

发布
暂无评论
iOS底层原理之—dyld与objc的关联