美文网首页
dyld和objc的关联

dyld和objc的关联

作者: Johnny_Z | 来源:发表于2020-10-14 00:22 被阅读0次

    一、objc


    查看objc源码的时候看到了void _objc_init(void)函数

    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
    }
    

    那这个方法什么时候进来的呢,我们打一个断点,通过lldbbt命令看一下堆栈

    image.png
    结合这张图我们得知_objc_init调用流程大致为:

    dyld的doModInitFunctions方法调用libSystem.B.dylib的libSystem_initializer方法;接着初始化了libdispatch; libdispatch又调用了_os_object_int,最终来到了_objc_init

    • 1、这里其实是各种初始化:环境初始化;静态变量初始化;运行时初始化;异常初始化;cache初始化等等
    • 2、这里最重要的是_dyld_objc_notify_register方法,他这里就是dyld的注册监听方法,用来跟objc关联的。

    二、dyld和objc关联过程

    1、_dyld_objc_notify_register解析

    • 1.1 在objc代码中我们看到它的声明
    //
    // 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);
    

    不知道大家有没有注意到源码中的注释+load(),这是一个小细节,初始化镜像的时候会调用在镜像中的+load()方法.

    • 1.2 在这个注释中说dyld将会调用mappedunmappedinitialized这些方法;既然是dyld调用那这些方法就应该会传到dyld中啊,顺着这条思路应该能想到_dyld_objc_notify_register的实现应该在dyld中,果不其然,在dyld3
    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped)
    {
        log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
    
        gAllImages.setObjCNotifiers(mapped, init, unmapped);
    }
    

    2、setObjCNotifiers解析

    void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
    {
        _objcNotifyMapped   = map;
        _objcNotifyInit     = init;
        _objcNotifyUnmapped = unmap;
       //.....此处省略
    }
    

    这三个函数指针会赋值给_objcNotifyMapped_objcNotifyInit_objcNotifyUnmapped这三个变量,其实这里就是写入注册函数

    3、map_images解析

    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_nolock函数看看,这里的核心代码是_read_images方法

        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
    
    • 3.1: 条件控制进行一次的加载
    • 3.2: 修复预编译阶段的 @selector 的混乱问题
    • 3.3: 查找类,错误混乱的类处理
    • 3.4: 修复重映射一些没有被镜像文件加载进来的 类
    • 3.5: 修复一些消息!
    • 3.6: 当我们类里面有协议的时候 : readProtocol
    • 3.7: 修复没有被加载的协议
    • 3.8: 分类处理
    • 3.9: 类的加载处理
    • 3.10: 没有被处理的类 优化那些被侵犯的

    4、readClass解析
    _read_images中有一个readClass方法的调用;

    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName = cls->mangledName();
        
        if (missingWeakSuperclass(cls)) {
            // No superclass (probably weak-linked). 
            // Disavow any knowledge of this subclass.
            if (PrintConnecting) {
                _objc_inform("CLASS: IGNORING class '%s' with "
                             "missing weak-linked superclass", 
                             cls->nameForLogging());
            }
            addRemappedClass(cls, nil);
            cls->superclass = nil;
            return nil;
        }
        
        cls->fixupBackwardDeployingStableSwift();
    
        Class replacing = nil;
        if (Class newCls = popFutureNamedClass(mangledName)) {
            // This name was previously allocated as a future class.
            // Copy objc_class to future class's struct.
            // Preserve future's rw data block.
            
            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.", 
                            cls->nameForLogging());
            }
            
            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro();
            memcpy(newCls, cls, sizeof(objc_class));
            rw->set_ro((class_ro_t *)newCls->data());
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->name);
            free((void *)old_ro);
            
            addRemappedClass(cls, newCls);
            
            replacing = cls;
            cls = newCls;
        }
        
        if (headerIsPreoptimized  &&  !replacing) {
            // class list built in shared cache
            // fixme strict assert doesn't work because of duplicates
            // ASSERT(cls == getClass(name));
            ASSERT(getClassExceptSomeSwift(mangledName));
        } else {
            addNamedClass(cls, mangledName, replacing);
            addClassTableEntry(cls);
        }
    
        // for future reference: shared cache never contains MH_BUNDLEs
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        
        return cls;
    }
    

    这一步会把class信息从二进制里面读出来,

    • newCls->data()取出来作rw
    • newCls->data()再取出来强转为class_ro_t *放到到rwro部分
    • addClassTableEntry这是将类插入到类的集合表中,为了后面调用的快速查找

    三、总结

    dyld和objc中是存在着一些交互的过程,需要关联起来进行研究学习。

    相关文章

      网友评论

          本文标题:dyld和objc的关联

          本文链接:https://www.haomeiwen.com/subject/tkeouktx.html