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

作者 : 开心源码 本文共5897个字,预计阅读时间需要15分钟 发布时间: 2022-05-13 共186人阅读

原文作者:我是小菜
原文链接:https://juejin.im/post/6884147035172405256

前言

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**********************************************************************/voidmap_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来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS底层原理之—dyld与objc的关联

发表回复