类的加载

173 阅读3分钟

我们用xcode写类,或者加载类的时候,有没有想过类里面的方法数据是如果被系统编译的,系统又是如何储存类里面的数据的?带着这些问题我们从源代码一步一步的分析:

流程分析 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();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

environ_init() ->主要是对环境变量的一些操作,读取影响运行时的环境变量,如果需要,会打印环境变量

tls_init(); ->对线程key的绑定 - 比如线程数据的析构函数

static_init();->运行C++静态构造函数。在dyld调用我们的静态构造函数之前,‘libc’会调用‘_objc_init()’,因此我们必须自己操作。

lock_init(),->是空实现,说明OC可以接受C或者C++的那套锁的机制,目前对我们没有什么帮助

exception_init();->异常初始化,主要是做异常回调,注册相应的异常回调机制

_dyld_objc_notify_register()重点

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

这个函数是dyld内存函数提供过来的,是针对于OC特有的方法;我们OC在整个环境中是一个运行时环境,运行时环境加载所有的类的信息的时候,又依赖于注册回调的通知,告诉我们dyld做了哪些事情,你需要哪些环境来作为彼此之间的通讯


map_images:read_images

map_images主要功能:

从镜像文件中读写到我们响应的内存里面来,里面的关键函数read_images来读取镜像文件中的类通过表进行储存

1 加载所有的类到类的'gdb_objc_realized_classes'表中

2 对所有的类做重映射

3 将所有sel都注册到namedSelectors 表中

4 修复函数指针遗留

5 将所有 'Protocol'都添加到'protocol_map'表中

6 对所有'Protocol'做重映射

7 初始化所有非懒加载的类,进行'rw','ro'等操作

8 遍历已标记的懒加载的类,并做初始化操作

9 处理所有'Category',包括'Class'和'Meta Class'

10 初始化所有未初始化的类

镜像读取存在两张表
1 gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
 2 allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);

gdb_objc_realized_classes 第一张表:只要存在于共享缓存里面,无论你是否实现都会存储到该表里面,只要不在dyld共享缓存中的以命名类的类表中

allocatedClasses 第二张表:只会存在于给分配过的类里面,通过objc_allocateClassPair已分配的所有类(和元类)的表

read_images内部read_class的作用: 判断是不是后期要处理的类,读取class的data(),如果要处理的话,对ro/rw进行处理,主要插入表


realizeClassWithoutSwift ()

  • 1读取class的data()
  • 2 创建ro/rw
  • 3 关联父类和元类
  • 4 将此类连接到其超类的子类列表中

methodizeClass :把ro的数据写入到rw里面

分析到这,分析的有点笼统,后面有更深理解的再进行补充

知识点补充

ro数据编译时存在, rw运行时存在,有时候ro有数据而rw没有数据是因为还没有初始化到里面,那为什么有了ro还有有rw呢? 可以这么理解ro是一份万年不变的代码,只能读取的代码,但是当我们调试的时候,外部需要进行添加或者动态处理一系列东西,为了不修改ro本质内容,所有需要添加到rw,例如runtime动态添加属性方法等