欢迎阅读iOS探索系列(按序阅读食用效果更加)
- iOS探索 alloc流程
- iOS探索 内存对齐&malloc源码
- iOS探索 isa初始化&指向分析
- iOS探索 类的结构分析
- iOS探索 cache_t分析
- iOS探索 方法的本质和方法查找流程
- iOS探索 动态方法解析和消息转发机制
- iOS探索 浅尝辄止dyld加载流程
- iOS探索 类的加载过程
- iOS探索 分类、类拓展的加载过程
- iOS探索 isa面试题分析
- iOS探索 runtime面试题分析
- iOS探索 KVC原理及自定义
- iOS探索 KVO原理及自定义
- iOS探索 多线程原理
- iOS探索 多线程之GCD应用
- iOS探索 多线程之GCD底层分析
- iOS探索 多线程之NSOperation
- iOS探索 多线程面试题分析
- iOS探索 细数iOS中的那些锁
- iOS探索 全方位解读Block
写在前面
在上一篇文章iOS探索 浅尝辄止dyld加载流程轻描淡写提了一句_objc_init
的_dyld_objc_notify_register
,本文将围绕它展开探索分析
一、_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();
// 保存 - libobjc - dyld
// C++ 怎么去做到通知
// 指针 - 回调 - 函数的地址
// 这里就是我们的数据 - images - objc lib
// dyld
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
1.environ_init方法
environ_init()
方法是初始化一系列环境变量,并读取影响运行时的环境变量
// 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
可以监控所有的+load
方法,从而处理启动优化OBJC_DISABLE_NONPOINTER_ISA
可以控制isa优化
开关,从而优化整个内存结构- 更多环境变量请终端输出
export OBJC_HELP=1
查看
2.tls_init方法
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++静态构造函数
(只会运行系统级别的构造函数)
在dyld调用静态构造函数之前libc
会调用_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方法
lock_init()
方法是个空函数,OC的锁机制完全采用C、C++那一套
void lock_init(void)
{
}
5.exception_init方法
exception_init()
初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
调用只声明不实现不作任何处理的方法,就会报错,来到_objc_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);
从_dyld_objc_notify_register
方法的注释中可以得出:
- 仅供objc运行时使用
- 注册处理程序,以便在映射、取消映射和初始化objc图像时调用
- dyld将会通过一个包含objc-image-info的镜像文件的数组回调
mapped
函数
_dyld_objc_notify_register
中的三个参数含义如下:
map_images
:dyld将image加载进内存时,会触发该函数load_image
:dyld初始化image会触发该函数unmap_image
:dyld将image移除时,会触发该函数
二、map_images->_read_images
当镜像加载到内存时map_image
会触发
/***********************************************************************
* 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_image
调用map_images_nolock
,其中hCount
表示镜像文件的个数,调用_read_images
来加载镜像文件
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);
}
firstTime = NO;
}
通过以上这些可以得出最核心的逻辑都在
_read_images
函数
1.创建表
通过doneOnce
一次创建两张表gdb_objc_realized_classes
、allocatedClasses
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);
ts.log("IMAGE TIMES: first time tasks");
}
gdb_objc_realized_classes
存储不在共享缓存且已命名的所有类,其容量是类数量的4/3allocatedClasses
存储已经初始化的类
// 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.类的重映射
从列表中取出所有类,遍历进行处理
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;
}
}
}
readClass
方法会返回Class
,跟进去看看具体实现(把目光放在所有返回值上)
- 当前类的父类中若有丢失的weak-linked类,则返回
nil
- 正常情况下不会走进
popFutureNamedClass
判断,这是专门针对未来的待处理的类的特殊操作
,因此也不会对ro、rw进行操作(可打断点调试,创建类和系统类都不会进入) - 在调用
addNamedClass
、addClassTableEntry
方法后返回cls
将当前类添加到已创建好的gdb_objc_realized_classes
哈希表(存放所有类)
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass 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());
}
当前类已经初始化,所以要添加到allocatedClasses
哈希表
static void addClassTableEntry(Class cls, bool addMeta = true) {
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
assert(!NXHashMember(allocatedClasses, cls));
if (!isKnownClass(cls))
NXHashInsert(allocatedClasses, cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
3.修复重映射
将未映射Class和Super Class重映射,调用_getObjc2ClassRefs
获取类的引用,调用_getObjc2SuperRefs
获取父类的引用,通过remapClassRef
进行重映射
// 将未映射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表
通过_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);
}
}
}
5.修复旧的函数指针调用遗留
通过_getObjc2MessageRefs
获取到静态段__objc_selrefs
,fixupMessageRef
遍历将函数指针进行注册,并fix为新的函数指针
// 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);
}
}
6.添加Protocol到协议表
调用_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);
}
}
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]);
}
}
8.实现非懒加载的类
苹果官方对于非懒加载类的定义是:
NonlazyClass is all about a class implementing or not a +load method.
所以实现了+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);
}
}
realizeClassWithoutSwift
分析:
①rw
初始化并将ro
拷贝一份到rw
中的ro
rw
表示readWrite
,由于动态性,可能会往类中添加属性、方法、添加协议ro
表示readOnly
,在编译时已经确定了内存
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);
}
②递归调用realizeClassWithoutSwift
完善继承链并处理当前类的父类
、元类
;如果有父类
,就通过addSubclass
把当前类
放到父类
的子类列表
中去
if (!cls) return nil;
...
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);
}
③当isa
找到根元类之后,根元类的isa是指向自己的,不会返回nil从而导致死循环——remapClass
中对类在表中进行查找的操作,如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
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 {
return c2;
}
}
④最后调用了methodizeClass
// Attach categories
methodizeClass(cls);
return cls;
⑤在methodizeClass
中,从ro
中读取方法列表(包括分类中的方法)、属性列表、协议列表
赋值给rw
// Install methods and properties that the class implements itself.
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*/);
⑥attachLists
是如何插入数据的呢?方法属性协议都可以直接通过attachLists
插入吗?
方法、属性
继承于entsize_list_tt
,协议
则是类似entsize_list_tt
实现,都是二维数组
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
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]));
}
}
从attachLists
的源码实现中可以得出:
-
(多对多)如果当前调用
attachLists
的list_array_tt
二维数组中有多个一维数组- 通过
realloc
对容器进行重新分配大小为原来的大小加上新增的大小 - 通过
memmove
把原来的数据移动到容器的末尾 - 把新的数据
memcpy
拷贝到容器的起始位置
- 通过
-
(0对一)如果调用
attachLists
的list_array_tt
二维数组为空且新增大小数目为 1- 直接赋值
addedList
的第一个list
- 直接赋值
-
(一对多)如果当前调用
attachLists
的list_array_tt
二维数组只有一个一维数组- 通过
realloc
对容器进行重新分配大小为原来的大小加上新增的大小 - 由于只有一个一维数组,所以直接赋值到新
Array
的最后一个位置 - 把新的数据
memcpy
拷贝到容器的起始位置
- 通过
而memmove
和memcpy
的区别在于:
- 在不知道需要平移的内存大小时,需要
memmove
进行内存平移,保证安全 memcpy
从原内存地址的起始位置开始拷贝若干个字节到目标内存地址中,速度快
9.实现懒加载类
前面已经提到了实现+load
方法的类就是非懒加载类
,那么没有实现的类就是懒加载类
也可以通过printf("non-lazy Class:%s\n",cls->mangledName())
去打印获取到所有非懒加载类
,发现只有实现了+load
的类才会被打印(FXPerson
内部实现了+load,其他都是系统内置的类)
那么懒加载类
是何时加到内存中的呢?
之所以叫懒加载类
还不是因为它懒嘛😆,用到的时候才会加到内存中
调用懒加载类
让他干活就是发送消息,仔细阅读lookUpImpOrForward
实现发现会有这么一顿操作
- 类没有被加载时,调用
realizeClassMaybeSwiftAndLeaveLocked
realizeClassMaybeSwiftAndLeaveLocked
调用realizeClassMaybeSwiftMaybeRelock
realizeClassMaybeSwiftMaybeRelock
调用realizeClassWithoutSwift
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if (!cls->isRealized()) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
...
}
static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
// Non-Swift class. Realize it now with the lock still held.
// fixme wrong in the future for objc subclasses of swift classes
realizeClassWithoutSwift(cls);
if (!leaveLocked) lock.unlock();
} else {
// Swift class. We need to drop locks and call the Swift
// runtime to initialize it.
lock.unlock();
cls = realizeSwiftClass(cls);
assert(cls->isRealized()); // callback must have provoked realization
if (leaveLocked) lock.lock();
}
return cls;
}
Q1:实现了+load
的子类ClassA
继承于没有+load
的父类ClassB
,ClassA
属于非懒加载类,在_read_images
时加载。那么ClassB
是什么呢?何时加载?
A1:ClassB
属于懒加载类
,在子类realizeClassWithoutSwift
递归时加载到内存(前文有提到realizeClassWithoutSwift会完善继承链并处理当前类的父类、元类)
Q2:父类A实现了+load
方法,子类B没有实现,那么子类B是懒加载类?何时加载?
A2:子类B
属于懒加载类
,父类A
干活跟子类B
没关系,用到时再加载
10.发现和处理所有Category
由于篇幅有限,将在下一篇文章中介绍
// 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);
// 首先,通过其所属的类注册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);
}
}
}
}
写在后面
本文主要讲了MachO中的数据是如何加载到内存的,有些细节点需要自己LLVM调试才会有所体会
下一篇文章会梳理分类的加载
和load_image
流程