通过上一篇的内容- > 底层探索-应用程序的加载之dyld,我们从dyld的角度,探索了程序的加载过程。
libObjc
在objc_init
方法中向dyld
注册了回调_dyld_objc_notify_register
,当dyld
把App
以及App
所依赖的一系列Mach-O
镜像加载到当前App
被分配的内存空间之后,dyld
会通知libObjc
来完成具体的加载工作。今天的主角就是objc_init
,我们将通过它来探究一下类的加载。
这里再啰嗦一句,大家看底层源码的时候一定要按主线流程去走,多看注释,多看注释,多看注释......
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
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);
}
通过注释,大概也了解了它的作用,引导程序初始化,注册回调通知,由libSystem调用,其实中间还进行了libdispatch的初始化。
一:_objc_init初始化准备
1. environ_init()
作用:环境变量初始化
* environ_init
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
void environ_init(void)
{
......省略
// Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
if (PrintHelp || PrintOptions) {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
}
}
方法顶部注释:1. 读取影响运行时的环境变量。2. 如果需要,还可以打印环境变量
我们可以越过判断条件将下面这段打印的代码提取出来,看看打印了什么
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
可以看到的确打印了各种各样的环境变量,当然也可以在终端输入
export OBJC_HELP=1
来查看更多的环境变量。我们同样可以在Xcode
中来配置一些环境变量来进行调试。
2. tls_init()
作用:对线程key的绑定
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
3. static_init()
作用:运行C++静态构造函数,需要注意的是只会运行系统级别的
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors,
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
4. lock_init()
注意:这是一个空函数,可能是为以后做预留
void lock_init(void)
{
}
5. exception_init()
作用:初始化异常处理,注册异常处理的回调,发生崩溃就会进入set_terminate
函数中
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
6:_dyld_objc_notify_register
_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);
从注释部分我们可以知道:
- 该方法只在objc运行期间中被使用;
- 当objc镜像文件被映射、初始化、取消映射关系时注册处理程序;
- dyld将会通过一个包含objc-image-info的镜像文件数组来回调mapped方法;
- 该函数方法被调用的时候,会调用mapped函数对已经加载的镜像文件处理;
- 当dyld调用initializers的时候回调用init方法;
在_dyld_objc_notify_register
中一共有三个参数:
map_images:
dyld
将image
加载进内存时触发load_images:
dyld
初始化image
时触发unmap_image:
dyld
将image
移除时触发
其实这三个参数都是函数指针,与dyld
中的_dyld_objc_notify_register方法中传入的参数一一对应。
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);
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
......省略部分
}
二:map_images
在上面已经提到当dyld
将image
加载到内存的时候会触发该方法的调用。而在map_images
中会调用map_images_nolock
,而在map_images_nolock
中,我们关注的重点必然是_read_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);
}
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
由于_read_images
代码过长,以下代码摘出重要部分,并加以注释
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
// 1.第一次进来 - 开始创建表
// gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
// allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
if (!doneOnce) {
doneOnce = YES;
......
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
// 2.读取所有类的列表
for (EACH_HEADER) {
classref_t *classlist = _getObjc2ClassList(hi, &count);
}
// 3.获取所有的类引用
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
}
// 4.sel - 方法编号
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
}
// 5.修复旧的objc_msgSend_fixup调用导致一些消息没有处理
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
}
// 6.协议
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);
}
}
// 7.修复协议重映射
// 获取所有的协议引用
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
// 🌞Realize non-lazy classes (for +load methods and static instances)
// 🌞8.实现非惰性类(用于+ load方法和静态实例)
for (EACH_HEADER) {
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
}
// 9.在CF基础上,实现未来类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 10.分类
for (EACH_HEADER) {
category_t **catlist = _getObjc2CategoryList(hi, &count);
}
}
下面我们依次对其中的内容进行分析
1. 创建两张表
if (!doneOnce) {
doneOnce = YES;
......
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
gdb_objc_realized_classes
存储所有的类,包括已实现和未实现并且在dyld共享缓存中不存在的类,其容量是类数量的4/3allocatedClasses
存储通过objc_allocateClassPair
已分配的所有类和元类的表
2. 读取所有的类
// 读取所有类的列表
for (EACH_HEADER) {
// 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
// ClassList 可以通过MachoView查看在sesion段下
classref_t *classlist = _getObjc2ClassList(hi, &count);
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
for (i = 0; i < count; i++) {
// 数组中会取出OS_dispatch_queue_concurrent,OS_xpc_object,NSRunloop等系统类,例如CF,Foundation,libdispatch中的类,以及自己创建的类
// 注意: 此时cls还不是类,是地址
Class cls = (Class)classlist[i];
// 通过readClass函数获取处理后的新类,内部主要操作ro和rw结构体
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 初始化所有懒加载的类需要的内存空间 - 现在数据没有加载到 - 连类都没有初始化的
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
其中readClass
也是重点,它的作用就是将类插入到表中。里面会对ro
,rw
进行相应的操作,但是需要注意的是:针对的是未来的类
/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* Returns the new class pointer. This could be:
* - cls
* - nil (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
* Note that all work performed by this function is preflighted by
* mustReadClasses(). Do not change this function without updating that one.
*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
......
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->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(getClass(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;
}
在
readClass
中会判断是否是后期要处理的类,是的话就读取class
的data()
,进行ro
,rw
处理会调用addNamedClass
的方法,并将类添加到总表当中,对应的代码是NXMapInsert(gdb_objc_realized_classes, name, cls)
/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClass(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getNonMetaClass uses name lookups. Classes not found by name
// lookup must be in the secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
assert(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// assert(!cls->isRealized());
}
3. 对所有的类做重映射
**注意:**一般走不到这里
// 将未映射Class和Super Class重映射,被remap的类都是非懒加载的类
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 重映射Class。注意从_getObjc2ClassRefs函数中取出类的引用
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
4. 添加SEL到namedSelectors表
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) 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的操作
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
在
sel_registerNameNoLock
方法中会将SEL添加到内存,例如,我们写代码时Xcode能够进行自动提示就是因为这个。
5. 修复旧的函数指针调用遗留
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
// 内部将常用的alloc,objc_msgsend等函数指针进行注册,并fix为新的函数指针
fixupMessageRef(refs+i);
}
}
6. 将所有的协议添加到protocal_map表中
调用_getObjc2ProtocolList
获取协议列表,遍历进行添加
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Person类,所有协议和对象的结构体都类似。isa都对应Protocal类
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
// 获取protocal哈希表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 从编译器中读取并初始化peotocal
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
7. 对所有的协议做重映射
同样是通过遍历的方式进行重映射
for (EACH_HEADER) {
// 注意,下面的函数是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolLists不一样,完全是两个不同的调用
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
8. 初始化所有非懒加载类,进行rw
,ro
的操作
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// hack for class __ARCLite__, which didn't get this above
#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
cls->cache._mask = 0;
cls->cache._occupied = 0;
}
if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache &&
(cls->ISA()->cache._mask || cls->ISA()->cache._occupied))
{
cls->ISA()->cache._mask = 0;
cls->ISA()->cache._occupied = 0;
}
#endif
addClassTableEntry(cls);
// 实现所以非懒加载的类(实例化类对象的一些信息,比如rw)
realizeClass(cls);
}
}
看到此处,恭喜你,其中realizeClass
将是我们本篇章的核心。
需要注意的是在起始位置系统有句注释:Realize non-lazy classes (for +load methods and static instances)
,这里有个概念:非懒加载类和懒加载类,官方给出的解释如下:
NonlazyClass is all about a class implementing or not a +load method.
意思是说要实现一个非懒加载的类,需要在类内部实现+load方法,那么实现了+load方法的类是非懒加载类(下面分析懒加载类的时候会加以验证),否则是懒加载类。明白了这两者的区别,下面我们来看下非懒加载类的加载流程。
8.1 realizeClass
由于代码过长,去掉了部分系统注释,更详细的代码大家可以自行查看源码
/***********************************************************************
* realizeClass
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClass(Class cls)
{
......省略部分
if (!cls) return nil; // 🌞递归调用的出口
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
// 🌞这里还是处理的未来类
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 🌞常规类的处理
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
// 🌞注意:此时只是初始化 rw 里面还没有内容
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
cls->chooseClassArrayIndex();
......省略部分
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// 🌞递归处理superClass和meta
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));
......省略部分
// Update superclass and metaclass in case of remapping
// 🌞父类与元类归属关系的处理
cls->superclass = supercls;
cls->initClassIsa(metacls);
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Connect this class to its superclass's subclass lists
// 🌞将此类连接到其超类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 🌞将ro数据写入rw
methodizeClass(cls);
return cls;
}
总结来说在realizeClass
做了这么几件事:
- 读取
class
的data()
,并进行ro/rw
的创建- 递归调用
realizeClass
,进行父类和元类的实现- 父类与元类归属关系的处理
- 将此类连接到其超类的子类列表
- 调用
methodizeClass
,将ro
数据写入rw
8.2 methodizeClass
/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
// 🌞读取到rw,此时rw还是没有值的
auto rw = cls->data();
auto ro = rw->ro;
......
// 🌞 读取 ro 的baseMethods,baseProperties,baseProtocols并调用
attachLists 将他们附加到 rw 当中
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
......省略
}
在
methodizeClass
中,会获取到rw
,并读取ro
的baseMethods
,baseProperties
,baseProtocols
并调用attachLists
将他们附加到rw
当中。
8.3 attachLists
在attachLists
处理了附加的逻辑
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
可以看出,此处也分三种情况处理:
- 多对多
many lists -> many lists
通过
realloc
对容器进行重新分配大小为原来的大小加上新增的大小 通过memmove
把原来的数据移动到容器的末尾 通过memcpy
把新的数据拷贝到容器的起始位置
- 0对1
0 lists -> 1 list
调用
addedLists
附加到第一个位置
- 1对多
1 list -> many lists
通过
realloc
对容器进行重新分配大小为原来的大小加上新增的大小 由于只有一个一维数组,所以直接赋值到新Array
的最后一个位置 把新的数据memcpy
拷贝到容器的起始位置
9. 遍历已标记的懒加载类,并进行初始化操作
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
// 实现懒加载的类
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
realizeClass
会处理懒加载的类,暂时先不展开,下个篇章将会进行分析
10. 处理所有的类别,包括class和metaClass
以下是精简后的代码,并已经加上相应注释
for (EACH_HEADER) {
// 外部循环遍历找到当前类,查找类对应的Category数组
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
// 内部循环遍历当前类的所有Category
category_t *cat = catlist[I];
Class cls = remapClass(cat->cls);
// 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 将Category添加到对应Class的value中,value是Class对应的所有category数组
addUnattachedCategoryForClass(cat, cls, hi);
// 将Category的method、protocol、property添加到Class
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
// 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
// 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
至此,map_images
已经分析完毕
11. _read_images小结
总结来说,在_read_images
中做了以下几件事情:
- 加载所以的类到类的
gdb_objc_realized_classes
表中- 对所有的类做重映射
- 将所有的SEL注册到哈希表中
- 修复函数指针遗留
- 将所有的协议添加到protocal_map表中
- 对所有的协议做重映射
- 初始化所有非懒加载类,进行
rw
,ro
的操作
🌞realizeClass
: 读取class
的data()
,并进行ro/rw
的创建。递归调用realizeClass
,进行父类和元类的实现。父类与元类归属关系的处理。将此类连接到其超类的子类列表。调用methodizeClass
,将ro
数据写入rw
。
🌞methodizeClass
: 获取到rw
,并读取ro
的baseMethods
,baseProperties
,baseProtocols
并调用attachLists
将他们附加到rw
当中。- 遍历已标记的懒加载类,并进行初始化操作
- 处理所有的类别,包括class和metaClass
- 初始化所有未初始化的类
三:懒加载类
在上面的内容当中我们对
normal class
即非懒加载类的加载过程进行了分析,那么future class
即懒加载类又是如何进行加载的呢?
1. 懒加载类的加载过程
按照常理来分析,懒加载必然是用到的时候才会去进行加载,什么时候会用到,那就是向这个类发送消息的时候。当我们创建一个对象的时候,会调用alloc
去开辟内存空间,在整个过程中必然会来到一个方法 --> lookUpImpOrForward
,翻看源码,我们发现其中有这么一句:
if (!cls->isRealized()) {
realizeClass(cls);
}
这里判断如果没有加载好,那就先加载一下类信息,而且
realizeClass
是否似曾相识,它不就是上个篇章中的核心么。非懒加载就是在其中进行加载的,这正好符合懒加载类在使用的时候才去初始化的特性。
(发现不同版本的源码不一样,我用的是objc4-750
,最新的源码可能方法名有所不同,最新的源码调用的是realizeClassWithoutSwift
)
在上面非懒加载类的加载过程中我们也已经分析过realizeClass
:
-
- 读取
class
的data()
,并进行ro/rw
的创建
- 读取
-
- 递归调用
realizeClass
,进行父类和元类的实现
- 递归调用
-
- 父类与元类归属关系的处理
-
- 将此类连接到其超类的子类列表
-
- 调用
methodizeClass
,将ro
数据写入rw
- 调用
通过分析我们可以得出这样的结论:懒加载类是第一次向类发送消息的时候加载到内存的
2. 实现+load会使懒加载类的加载提前
此时懒加载类会变成非懒加载类,下面我们用代码加以验证。首先看下我们的测试代码。共有WYPerson
,WYWorker
,WYTeacher
三个类,WYPerson
未实现+load
方法,后两者皆实现+load
方法
read_image
中的处理非懒加载类的地方,打印出所有非懒加载的类
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
// 🐵🌹打印所有的非懒加载类
printf("_getObjc2NonlazyClassList Class:%s\n",cls->mangledName());
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);
}
}
通过如图的打印,验证了我们的结论:当一个懒加载的类实现了+load方法,它的加载将会提前,变成一个非懒加载的类
四:总结
- 非懒加载类是在
runtime
启动时map_images
方法执行期间就已经初始化完成; - 懒加载类是在其第一次使用,也就是第一次发送消息时对类进行初始化的时候加载到内存的。
+load
方法会使懒加载类的加载提前。
下一篇章会对分类进行相应的分析,敬请期待!