load_imags和map_images探索

816 阅读3分钟

_objc_init解析

在objc源码中看到调用_dyld_objc_notify_register前有几个初始化方法如下:

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(); //创建线程的析构函数,tls线程的局部存储
    static_init(); // 运行C++的静态构造函数,并且在dyld调用objc_init函数之前调用
    runtime_init(); //分类表的初始化和类表的初始化
    exception_init();//异常处理的初始化,如数组越界等异常
#if __OBJC2__
    cache_t::init(); //缓存的初始化
#endif
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

environ_init

environ_init方法中我们可以通过设置OBJC_HELP 和 OBJC_PRINT_OPTIONS,可以在XCode->product->scheme->edit scheme如下设置: image.png 就会打印很多环境变量,环境变量说明: 环境变量说明.png

load_images

  • 懒加载类:没有重写load方法
  • 非懒加载类:重写了load方法

load_images方法说明

Xnip2022-05-19_23-24-04.jpg

prepare_load_methods方法

Xnip2022-05-19_23-35-16.jpg

schedule_class_load方法

Xnip2022-05-19_23-28-27.jpg

call_load_methods方法

Xnip2022-05-19_23-42-39.jpg

结论

  • load_imges是找到所有的load方法并且去执行;
  • 先找本来的load方法,再找父类的load方法,最后找分类的load方法,所以先执行父类,在执行子类,最后执行分类;
  • 在写load方法的时候不能写[super load],会自动执行父类load方法;
  • 如果有多个分类中有load,会按照编译顺序去执行分类的load方法;
  • load方法是直接调用;
  • load方法是线程安全的;

map_images探索

map_images 进入map_images_nolock函数中

map_images_nolock说明

  • preopt_init()函数,共享缓存优化
  • _objc_inform 打印已经加载的image
  • 找到所有的类,可执行文件或者动态库都是以mach-o文件格式,是一堆的二进制数据,他们的头部都有一些配置文件,描述存储代码的位置大小,常量数据的位置大小,dyld从头部去读取,找到所有的类;
  • sel_init 初始化C++的构造方法和析构方法;
  • arr_init 初始化自动释放池、散列表、关联对象的初始化
  • _read_images

_read_images说明

  1. 开启non-pointer isa,指针优化开启,优化NSNumber等的tagged pointer优化,小对象优化,加载所有类到类的gdb_objc_realized_classes(dyld 共享缓存数据的表)表中。
  2. 对所有类做重映射。
  3. 将所有SEL都注册到namedSelectors表中。
  4. 修复函数指针遗留。
  5. 将所有Protocol都添加到protocol_map表中。
  6. 对所有Protocol做重映射。
  7. 初始化所有非懒加载的类,进行rw、ro等操作。
  8. 遍历已标记的懒加载的类,并做初始化操作。
  9. 处理所有Category,包括Class和Meta Class。
  10. 初始化所有未初始化的类。

readClass

在 discover classes中调用 readClass 方法 读取所有类并且调用addClassTableEntry 方法,加入到类表中; 可以加入如下方法,分辨自己的写的类: image.png

初始化所有非懒加载的类

源码如下:

// Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;

            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage
            }
            realizeClassWithoutSwift(cls, nil);
        }
    }

    ts.log("IMAGE TIMES: realize non-lazy classes");

realizeClassWithoutSwift

这个方法是-类的初始化操作,关键代码

  • 给rw开辟空间,将ro中的数据‘拷贝’到rw中: Xnip2022-05-23_09-46-22.jpg
  • 递归调⽤realizeClassWithoutSwift,对⽗类和元类进⾏初始化
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
  • 设置类的父类和isa指针
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
  • 处理分类
// Attach categories
methodizeClass(cls, previously);

懒加载类的加载流程

懒加载类也会调用运行到realizeClassWithoutSwift方法中,当我们使用懒加载类中,我们才能看到他调用realizeClassWithoutSwift,第一次接受到消息的时候才会被加载;