上篇文章9.iOS底层探索之APP加载流程(DYLD)末尾提到了_objc_init,这里我们来展开分析
一、_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();
/// c++静态构造方法
static_init();
/// 锁的处理,里面是空的,有可能未开源或者说兼容c++那一套
lock_init();
/// 初始化异常处理系统
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
1.environ_init读取影响运行时的环境变量。如果需要,还可以打印环境变量help。
/***********************************************************************
* 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);
}
}
}



OBJC_PRINT_LOAD_METHODS设置为YES就可以打印所有实现了load方法的类,从而处理启动优化,可以在终端输入export OBJC_HELP=1来查看所有系统给我们提供的环境变量以及它们的作用,也可以参考文章
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
static_init运行c++静态构造函数(只运行系统级别的)。libc在dyld调用静态构造函数之前调用_objc_init(),所以我们必须自己做。
/***********************************************************************
* 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
锁的处理,里面是空实现,有可能未开源或者说兼容c、c++那一套
void lock_init(void)
{
}
5.exception_init
初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
出现异常,就会来到这里_objc_terminate
/***********************************************************************
* _objc_terminate
* Custom std::terminate handler.
*
* The uncaught exception callback is implemented as a std::terminate handler.
* 1. Check if there's an active exception
* 2. If so, check if it's an Objective-C exception
* 3. If so, call our registered callback with the object.
* 4. Finally, call the previous terminate handler.
**********************************************************************/
static void (*old_terminate)(void) = nil;
static void _objc_terminate(void)
{
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: terminating");
}
if (! __cxa_current_exception_type()) {
// No current exception.
(*old_terminate)();
}
else {
// There is a current exception. Check if it's an objc exception.
@try {
__cxa_rethrow();
} @catch (id e) {
// It's an objc object. Call Foundation's handler, if any.
(*uncaught_handler)((id)e);
(*old_terminate)();
} @catch (...) {
// It's not an objc object. Continue to C++ terminate.
(*old_terminate)();
}
}
}
6._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函数
map_images:dyld将image加载进内存时,会触发该函数load_image:dyld初始化image会触发该函数unmap_image:dyld将image移除时,会触发该函数 。
二、_read_images
dyld将image加载进内存时,会触发map_images函数
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
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又调用了map_images_nolock
void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[])
{
...
if (hCount > 0) {///hCount是镜像文件的个数,调用_read_images来加载镜像文件
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
...
}
下面重点来了_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]);
}
}
// 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.创建2张表
if (!doneOnce) {
doneOnce = YES;
...
/// 初始化 TaggedPointer
initializeTaggedPointerObfuscator();
// 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);
ts.log("IMAGE TIMES: first time tasks");
}
gdb_objc_realized_classes: 存储不在共享缓存且已命名的所有类,无论是否已实现,其容量是类数量的4/3allocatedClasses: 存储通过objc_allocateClassPair已分配的所有类和元类的表
// This is a misnomer: gdb_objc_realized_classes is actually a list of
// named classes not in the dyld shared cache, whether realized or not.
NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
/***********************************************************************
* allocatedClasses
* A table of all classes (and metaclasses) which have been allocated
* with objc_allocateClassPair.
**********************************************************************/
static NXHashTable *allocatedClasses = nil;
2.发现所有的类,修复未解决的未来类。标bundle类。
主要操作是把发现的类存入2张表里
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
for (EACH_HEADER) {
// 从编译后的类列表中取出所有类,获取到的是一个classref_t类型的指针
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、Fundation、libdispatch中的类。以及自己创建的类
Class cls = (Class)classlist[i];
// 通过readClass函数获取处理后的新类
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;
}
}
}
ts.log("IMAGE TIMES: discover classes");
readClass会返回一个新类,我们进去看看源码
/***********************************************************************
* 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)
{
const char *mangledName = cls->mangledName();
...
Class replacing = nil;
///🌹处理future class
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(getClassExceptSomeSwift(mangledName));
} else {
///🌹存类到表中
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
...
return cls;
}
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
...
NXMapInsert(gdb_objc_realized_classes, name, cls);
...
}
static void addClassTableEntry(Class cls, bool addMeta = true) {
...
if (!isKnownClass(cls))///添加类到表allocatedClasses
NXHashInsert(allocatedClasses, cls);
if (addMeta) ///添加元类到表allocatedClasses
addClassTableEntry(cls->ISA(), false);
}
3.修复类重映射 一般走不进来
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.
// 主要是修复重映射 - 一般走不进来
// 将未映射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]);
}
}
}
ts.log("IMAGE TIMES: remap classes");
4.添加SEL到namedSelectors表
通过_getObjc2SelectorRefs拿到MachO中的静态段__objc_selrefs,遍历列表调用sel_registerNameNoLock将SEL添加到namedSelectors哈希表
// 将所有SEL都注册到哈希表中,是另外一张哈希表
// Fix up @selector references
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);
}
}
}
ts.log("IMAGE TIMES: fix up selector references");
看看sel_registerNameNoLock源码
SEL sel_registerNameNoLock(const char *name, bool copy) {
return __sel_registerName(name, 0, copy); // NO lock, maybe copy
}
static SEL __sel_registerName(const char *name, bool shouldLock, bool copy)
{
...
if (!namedSelectors) {
namedSelectors = NXCreateMapTable(NXStrValueMapPrototype,
(unsigned)SelrefCount);
}
if (!result) {
result = sel_alloc(name, copy);
// fixme choose a better container (hash not map for starters)
NXMapInsert(namedSelectors, sel_getName(result), result);
}
return result;
}
5.修复旧的函数指针调用遗留
通过_getObjc2MessageRefs获取到静态段__objc_selrefs,fixupMessageRef遍历将函数指针进行注册,并fix为新的函数指针
#if SUPPORT_FIXUP
// Fix up old objc_msgSend_fixup call sites
// 修复旧的函数指针调用遗留
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);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
6.将所有的Protocol都添加到protocol_map表中
调用_getObjc2ProtocolList获取到__objc_protolist协议列表,readProtocol遍历添加Protocol到protocol_map哈希表
// Discover protocols. Fix up protocol refs.
// 遍历所有协议列表,并且将协议列表加载到Protocol的哈希表中
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Protocol类,所有协议和对象的结构体都类似,isa都对应Protocol类
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
// 获取protocol哈希表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// 从编译器中读取并初始化Protocol
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
ts.log("IMAGE TIMES: discover protocols");
7.修复协议列表引用
通过_getObjc2ProtocolRefs获取到__objc_protorefs(与__objc_protolist不是同一个东西),遍历remapProtocolRef修复协议,remapProtocolRef比较当前协议和协议列表中同一内存地址的协议是否相同,如果不同则替换
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
// 修复协议列表引用,优化后的images可能是正确的,但是并不确定
for (EACH_HEADER) {
// 需要注意到是,下面的函数是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不一样
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
ts.log("IMAGE TIMES: fix up @protocol references");
8.实现非懒加载的类(实现了load方法)
所以实现了+load方法的类是非懒加载类,否则就是懒加载类
_getObjc2NonlazyClassList获取到__objc_nlclslist数据段,取出非懒加载类
addClassTableEntry再加载一遍,如果已添加就不会添加进去,确保整个结构都被添加
realizeClassWithoutSwift实例化类对象的一些信息
// Realize non-lazy classes (for +load methods and static instances)
// 实现非懒加载的类,对于load方法和静态实例变量
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
// printf("non-lazy Class:%s\n",cls->mangledName());
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);
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
}
// 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
重点看下realizeClassWithoutSwift
8.1 realizeClassWithoutSwift
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
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 {///🌹🌹🌹正常类的处理 rw初始化 将ro拷贝一份到rw中的ro
// 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);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
...
///🌹🌹🌹递归实现父类和元类 s
upercls递归的出口在上面if (!cls) return nil; 根类继承nil,
metacls递归的出口在remapClass
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
...
// Update superclass and metaclass in case of remapping
///🌹🌹🌹更新父类和元类的归属关系
cls->superclass = supercls;
cls->initClassIsa(metacls);
...
// Connect this class to its superclass's subclass lists
///🌹🌹🌹将这个类连接到它的超类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
///🌹🌹🌹附加类别
methodizeClass(cls);
return cls;
}
static Class remapClass(Class cls) {
runtimeLock.assertLocked();
Class c2;
if (!cls) return nil;
NXMapTable *map = remappedClasses(NO);
/// 🌹🌹🌹如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
if (!map || NXMapMember(map, cls, (void**)&c2) == NX_MAPNOTAKEY) {
return cls;
} else {
/// c2为nil
return c2;
}
}
realizeClassWithoutSwift主要做了:
- 初始化
rw并将ro拷贝一份到rw - 递归实现父类和元类
- 更新父类和元类的归属关系
- 将这个类连接到它的超类的子类列表
methodizeClass附加类别
8.2 methodizeClass
修复cls的方法列表、协议列表和属性列表,附上任何未完成的类别。
/***********************************************************************
* 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)
{
...
// Install methods and properties that the class implements itself.
/// 🌹🌹🌹从ro中读取方法列表(包括分类中的方法)、属性列表、协议列表赋值给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*/);
...
}
8.3 attachLists
attachLists可以添加方法,属性,协议,说明他们内部的结构都是差不多的
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3>
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0>
struct protocol_list_t {
uintptr_t count;// count is 64-bit by accident.
protocol_ref_t list[0]; // variable-size

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;5
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]));
}
}
attachLists可以分为3中情况:
- (多对多)当前调用
attachLists的list_array_tt二维数组中有多个一维数组
通过`realloc`对容器进行重新分配大小为原来的大小加上新增的大小
通过`memmove`把原来的数据移动到容器的末尾
通过`memcpy`把新的数据拷贝到容器的起始位置
- (0对1)如果调用
attachLists的list_array_tt二维数组为空且新增大小数目为 1
直接赋值addedList的第一个list
- (1对多)如果调用
attachLists的list_array_tt二维数组只有一个一维数组
通过realloc对容器进行重新分配大小为原来的大小加上新增的大小
由于只有一个一维数组,所以直接赋值到新Array的最后一个位置
把新的数据memcpy拷贝到容器的起始位置
memmove和memcpy的区别:
- 在不知道需要平移的内存大小时,需要
memmove进行内存平移,保证安全 memcpy从原内存地址的起始位置开始拷贝若干个字节到目标内存地址中,速度快
9.实现所有懒加载的类
// Realize newly-resolved future classes, in case CF manipulates them
// 遍历resolvedFutureClasses数组,实现所有懒加载的类
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);
}
ts.log("IMAGE TIMES: realize future classes");
由于懒加载类并没有实现load方法,所以不需要在启动的时候就加载到内存中,那么懒加载类是什么时候加载到内存中的呢?
非懒加载类会递归寻找父类,并进行加载,所以如果一个懒加载类是父类,则一定会在递归中被加载,注意子类并不会哦
如果这个类调用了方法,进行了消息的发送,说明此时是肯定已经加载的。
寻找函数lookUpImpOrForward,我们发现有一个!cls->isRealized()判断,是判断类有没有加载
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
发现如下调用流程:
lookUpImpOrForward -> realizeClassMaybeSwiftAndLeaveLocked -> realizeClassMaybeSwiftMaybeRelock -> realizeClassWithoutSwift
小结:懒加载类的加载,是在第一次被调用(发送消息)的时候才进行加载,并不是启动的时候就加载进内存
10.发现和处理所有Category
分类必须在最后处理,以避免潜在的竞争,分类的加载分了4中情况,篇幅比较长,将会在下篇文字展开分析
// Discover categories.
// 发现和处理所有Category
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);
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
// 首先,通过其所属的类注册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;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
// 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
// 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
ts.log("IMAGE TIMES: discover categories");
// Category discovery MUST BE LAST to avoid potential races
// when other threads call the new category code before
// this thread finishes its fixups.
// +load handled by prepare_load_methods()
// 初始化从磁盘中加载的所有类,发现Category必须是最后执行的
tips
rw表示readWrite,由于动态性,可能会往类中添加属性、方法、协议ro表示readOnly,在编译时已经确定了内存