类的加载(上)

Posted WeaterMr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的加载(上)相关的知识,希望对你有一定的参考价值。

类的加载(上)

_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();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    // map_images()
    // load_images()
#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif

  • environ_init() : 读取影响运行时的环境变量。如果需要,还可以打印环境变量帮助。

  • tls_init() 关于线程key的绑定 - 比如每线程数据的析构函数

  • static_init() 运行C ++静态构造函数。在dyld调用我们的静态构造函数之前,libc 会调用 _objc_init(), 因此我们必须自己做

  • lock_init(): 没有重写,采用C++ 的特性

  • exception_init () 初始化libobjc的异常处理系统

  • cache_init() 缓存条件初始化

  • runtime_init() : runtime运行时环境初始化,里面主要
    是:unattachedCategories,allocatedClasses 后面会分析

  • _imp_implementationWithBlock_init :启动回调机制。通常这不会做什么,因为所有的初始化都
    是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib。

环境变量的获取与作用

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) 
        const option_t *opt = &Settings[i];
        _objc_inform("%s: %s", opt->env, opt->help);
        _objc_inform("%s is set", opt->env);
    
bjc[1835]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[1835]: OBJC_PRINT_IMAGES is set
objc[1835]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[1835]: OBJC_PRINT_IMAGE_TIMES is set
objc[1835]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[1835]: OBJC_PRINT_LOAD_METHODS is set
objc[1835]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[1835]: OBJC_PRINT_INITIALIZE_METHODS is set
objc[1835]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[1835]: OBJC_PRINT_RESOLVED_METHODS is set
objc[1835]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[1835]: OBJC_PRINT_CLASS_SETUP is set
objc[1835]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[1835]: OBJC_PRINT_PROTOCOL_SETUP is set
objc[1835]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[1835]: OBJC_PRINT_IVAR_SETUP is set
objc[1835]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables

我们可以通过一些环境变量做一些特殊处理。

上图第一个环境变量是获取干净的类信息。
上图第二个环境变量是打印所有的 + load 方法,有利于我们做程序启动优化。

_dyld_objc_notify_register

_dyld_objc_notify_register(&map_images, load_images, unmap_image);该函数的两个参数,
1.&map_images 表示指针传递,和内部调用的函数保持同步。&map_images主要是映射镜像文件,耗时操作。管理文件中和动态库中所有的符号(class Protocol selector category)
2.load_image 加载执行load方法

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

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
    if (hCount > 0) 
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    

_read_images函数

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)

    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;
    Class *resolvedFutureClasses = nil;
    size_t resolvedFutureClassCount = 0;
    static bool doneOnce;
    bool launchTime = NO;
    TimeLogger ts(PrintImageTimes);

    runtimeLock.assertLocked();

    ts.log("IMAGE TIMES: first time tasks");
     。。。
    ts.log("IMAGE TIMES: fix up selector references");
    。。。
    ts.log("IMAGE TIMES: discover classes");
    。。。
    ts.log("IMAGE TIMES: remap classes");
    。。。
    ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
    。。。
    ts.log("IMAGE TIMES: discover protocols");
    。。。
    ts.log("IMAGE TIMES: fix up @protocol references");
    。。。
    ts.log("IMAGE TIMES: discover categories");
    。。。
    ts.log("IMAGE TIMES: realize non-lazy classes");
    。。。
    ts.log("IMAGE TIMES: realize future classes");

    。。。
     _objc_inform("PREOPTIMIZATION: %zu selector references not "
                     "pre-optimized", UnfixedSelectors);
     _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",
                     PreoptOptimizedMethodLists, 

通过源码分析可以得到以下流程:
• • • • • • • • • •

  • 1: 条件控制进行一次的加载
  • 2: 修复预编译阶段的 @selector 的混乱问题
  • 3: 错误混乱的类处理
  • 4:修复重映射一些没有被镜像文件加载进来的类
  • 5: 修复一些消息!
  • 6: 当我们类里面有协议的时候 : readProtocol 7: 修复没有被加载的协议
  • 8: 分类处理重点
  • 9: 类的加载处理重点
  • 10 : 没有被处理的类 优化那些被侵犯的类
    static size_t UnfixedSelectors;
    
        mutex_locker_t lock(selLock);
        for (EACH_HEADER) 
            if (hi->hasPreoptimizedSelectors()) continue;

            bool isBundle = hi->isBundle();
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) 
                const char *name = sel_cname(sels[i]);
                SEL sel = sel_registerNameNoLock(name, isBundle);
                if (sels[i] != sel) 
                    sels[i] = sel;
                
            
        
    

这里的sel 即为名称加地址,sel所在的位置不同进行相关的修复。

核心函数 readClass

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)

    const char *mangledName = cls->nonlazyMangledName();
    

我们可以通过判断 mangledName 和我们要研究的类是否相同,然后做特殊的处理。然后将断电断到条件中,就可以看当前我们要研究的类的流程。
未完待续。。。

以上是关于类的加载(上)的主要内容,如果未能解决你的问题,请参考以下文章

多次加载的 Swift 惰性变量(计算属性?)

错误:惰性类的 Getter 不能是最终的 Kotlin Spring Boot

Kendo UI:Kendo 网格的惰性绑定

为啥加载惰性集合

通过惰性列表加载图像?

如何使用 SQLAlchemy 找出是不是还没有加载惰性关系?