上篇文章應(yīng)用程序加載分析了dyld到main()函數(shù)的大體流程,這篇文章主要分析_objc_init()到類的處理流程。
1._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?
// 讀取影響運(yùn)行時(shí)的環(huán)境變量,源碼工程中可打印。
environ_init();
// 關(guān)于線程key的綁定 比如線程數(shù)據(jù)的析構(gòu)函數(shù)
tls_init();
// 運(yùn)行 C++ 靜態(tài)構(gòu)造函數(shù)。在dyld調(diào)用我們的靜態(tài)構(gòu)造函數(shù)之前,libc會(huì)調(diào)用 _objc_init(),因此需要自己做
static_init();
// runtime運(yùn)行時(shí)環(huán)境初始化,主要是unattachedCategories、allocatedClasses方法
runtime_init();
// 初始化 libObjc的異常處理系統(tǒng)
exception_init();
// 緩存條件初始化
cache_init();
// 啟動(dòng)回調(diào)機(jī)制,通常不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘模菍?duì)于某些進(jìn)程我們會(huì)迫不及待的加載trampolines dylib。
_imp_implementationWithBlock_init();
// 注冊(cè)回調(diào) map_images 管理文件和動(dòng)態(tài)庫(kù)中的所有符號(hào)(class Protocol selector category) load_image 加載執(zhí)行l(wèi)oad方法
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
環(huán)境變量可通過(guò)修改源碼工程代碼打?。?/p>
void environ_init(void)
{
....省略
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
_objc_inform("%s: %s", opt->env, opt->help);
_objc_inform("%s is set", opt->env);
}
}
打印結(jié)果:

OBJC_PRINT_LOAD_METHODS 設(shè)置了會(huì)打印所有執(zhí)行+(void)load方法的類,舉個(gè)??


2._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);
通過(guò)注釋可知:
- 僅供
objc 運(yùn)行時(shí)使用。 - 注冊(cè)回調(diào),當(dāng)鏡像文件被
mapped--映射--&map_images、unmapped--取消映射--unmap_image、init--初始化--load_images。
先看下map_images方法,通過(guò)源碼分析可知其核心方法為map_images->map_images_nolock-> _read_images,源碼如下:
/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked
* list beginning with headerList.
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
...
// 1.控制一次性加載條件
if (!doneOnce) {
...
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 創(chuàng)建哈希表 儲(chǔ)存不在共享緩存且已命名的類,不論是否實(shí)現(xiàn),容量為類容量的3/4.
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
...
}
// Fix up @selector references
// 2.修復(fù)預(yù)編譯階段@selector錯(cuò)亂問(wèn)題 sel是帶地址的字符串
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
// 拿到Mach-O中的靜態(tài)段__objc_selrefs
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 注冊(cè) sel
SEL sel = sel_registerNameNoLock(name, isBundle);
// 如果地址不相同改為一致
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
...
// 3、錯(cuò)誤混亂的類處理
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
// 從Mach-O中獲取靜態(tài)段__objc_classlist
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
// 此時(shí)類只是一個(gè)地址
Class cls = (Class)classlist[i];
// 變?yōu)轭惷?地址
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 經(jīng)過(guò)調(diào)試沒(méi)有進(jìn)入
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.
// 將懶加載類添加到數(shù)組
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
...
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.
// 4、修復(fù)重映射一些沒(méi)有被鏡像文件加載進(jìn)來(lái)的類 經(jīng)調(diào)試沒(méi)有進(jìn)入
if (!noClassesRemapped()) {
for (EACH_HEADER) {
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]);
}
}
}
...
// 5、修復(fù)一些消息
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++) {
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
...
// 6、當(dāng)類里面有協(xié)議時(shí):readProtocol 讀取協(xié)議 遍歷尋找所有的協(xié)議列表添加到protocol_map哈希表
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
// definition
if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
bool isBundle = hi->isBundle();
// 獲取到Mach-O中的靜態(tài)段__objc_protolist協(xié)議列表,存入protocol_map表
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
...
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
//7、修復(fù)沒(méi)有被加載的協(xié)議
for (EACH_HEADER) {
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
continue;
// 獲取到Mach-O的靜態(tài)段 __objc_protorefs
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
// 比較協(xié)議內(nèi)存地址是否相同,不同則替換
remapProtocolRef(&protolist[i]);
}
}
...
// 8、分類處理
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes. rdar://problem/53119145
// 需要在分類初始化將數(shù)據(jù)加載到類后才執(zhí)行,對(duì)于運(yùn)行時(shí)出現(xiàn)的分類,將分類的發(fā)現(xiàn)推遲到對(duì)_dyld_objc_notify_register調(diào)用完成后地方第一個(gè)load_images調(diào)用為止
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
...
// Category discovery MUST BE Late to avoid potential races
// when other threads call the new category code before
// this thread finishes its fixups.
// +load handled by prepare_load_methods()
// Realize non-lazy classes (for +load methods and static instances)
// 9、類的加載處理
for (EACH_HEADER) {
// 獲取Mach-O的靜態(tài)段__objc_nlclslist非懶加載類表
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 插入當(dāng)前類
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
}
// 實(shí)現(xiàn)類上部分的cls為類名+地址但是類數(shù)據(jù)并沒(méi)初始化。
realizeClassWithoutSwift(cls, nil);
}
}
...
// 10、沒(méi)有被處理的類,優(yōu)化那些被侵犯的類
// Realize newly-resolved future classes, in case CF manipulates them
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");
}
// 實(shí)現(xiàn)類
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) {
// 實(shí)現(xiàn)所有類
realizeAllClasses();
}
...
}
第三步的readClass方法,走過(guò)之后打印

看下源碼:
/***********************************************************************
* 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();
//當(dāng)前類的父類中若有丟失的weak-linked類,則返回nil
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
// 判斷是否是未來(lái)要處理的類(斷點(diǎn)后發(fā)現(xiàn)不會(huì)走)
if (Class newCls = popFutureNamedClass(mangledName)) {
... ro rw處理
}
// 判斷類是否已經(jīng)加載到內(nèi)存
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); // 插入表 machO-->內(nèi)存
}
// 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;
}
mangledName方法源碼:
const char *mangledName() {
// fixme can't assert locks here
ASSERT(this);
// 已經(jīng)實(shí)現(xiàn)或者未來(lái)類,類名從rw->ro()->name
if (isRealized() || isFuture()) {
return data()->ro()->name;
} else {
//從ro中獲取
return ((const class_ro_t *)data())->name;
}
}
addNamedClass源碼分析:
/***********************************************************************
* 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 = 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 {
// 插入到gdb_objc_realized_classes哈希表 在runtimeinit()方法中創(chuàng)建
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());
}
獲取到類名之后把類名插入gdb_objc_realized_classes哈希表。

readClass總結(jié): 只是把類從machO取出讀到內(nèi)存,插入表中,獲取到的類為地址 + 名字。
realizeClassWithoutSwift 方法
realizeClassWithoutSwift主要是下面三個(gè)步驟:
1.處理data(),將data()---ro替換為rw,并把ro拷貝到rw -->ro
2.遞歸調(diào)用realizeClassWithoutSwift完善類結(jié)構(gòu)和繼承連關(guān)系。
3.methodizeClass處理方法。
1 處理data()數(shù)據(jù)
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls; // 如果是已實(shí)現(xiàn)類(data()讀出來(lái)是rw) 直接返回
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 取出類data()此時(shí)為class_ro_t
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META; //是否是元類
if (ro->flags & RO_FUTURE) { // 是否是未來(lái)類
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = objc::zalloc<class_rw_t>(); //rw分配空間
rw->set_ro(ro); //設(shè)置rw中的ro
rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 設(shè)置flag RW_REALIZED class_t->data 是rw非ro RW_REALIZING 類已經(jīng)實(shí)現(xiàn)但未完成
cls->setData(rw); //data()設(shè)置為rw
}
...
-
ro為readOnly在編譯時(shí)確定了內(nèi)存,包含類名、方法、協(xié)議、和實(shí)例變量等信息,由于是只讀的,被稱為clean Memory,值加載后不會(huì)發(fā)生改變的內(nèi)存。 -
rw表示readWrite,由于動(dòng)態(tài)性,可能會(huì)往類中添加屬性,方法、協(xié)議,在2020WWDC中關(guān)于內(nèi)存優(yōu)化提到rw,只有10%的類改變了rw,所以有的rwe,類的額外可讀可寫結(jié)構(gòu),rw為可讀可寫,所有為dirty Memory,運(yùn)行時(shí)可能會(huì)添加方法或者屬性等。
2.遞歸調(diào)用 realizeClassWithoutSwift 完善類結(jié)構(gòu)和繼承連關(guān)系。
// 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.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 遞歸調(diào)用 確定繼承連關(guān)系 實(shí)現(xiàn)父類和元類 remapClass中當(dāng)找到NSObject返回nil打破循環(huán)
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
// 根元類isa指向自身 通過(guò)if (cls->isRealized()) return cls 打破循環(huán)。
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
...
// 完善類結(jié)構(gòu) 賦值父類和isa指向元類
cls->superclass = supercls;
cls->initClassIsa(metacls);
....
// Connect this class to its superclass's subclass lists
if (supercls) {
// 雙向綁定父類子類關(guān)系
addSubclass(supercls, cls);
} else {
// 綁定NSObjcet或者別的沒(méi)有父類的根類,父類指向nil
addRootClass(cls);
}
....
遞歸調(diào)用realizeClassWithoutSwift 循環(huán)設(shè)置父類和元類類,經(jīng)測(cè)試最先走的類是NSObject元類,然后是NSObject類。
static Class remapClass(Class cls)
{
runtimeLock.assertLocked();
if (!cls) return nil;
auto *map = remappedClasses(NO);
if (!map)
return cls;
auto iterator = map->find(cls);
if (iterator == map->end())
return cls;
return std::get<1>(*iterator);
}
3.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, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
// 添加方法列表、屬性列表、協(xié)議列表到rw中
// 方法列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls)); //根據(jù)sel地址,對(duì)方法列表排序
if (rwe) rwe->methods.attachLists(&list, 1);
}
// 屬性列表
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
// 協(xié)議列表
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->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 根元類添加initialize方法
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.加入分類方法
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
...
}
methodizeClass方法主要是為類添加方法、屬性、協(xié)議,當(dāng)當(dāng)前調(diào)用者為類的時(shí)候,只會(huì)對(duì)rw->ro()的方法進(jìn)行排序(按照sel地址),此時(shí)取出的是ro,不是rwe。
attachToClass方法
void attachToClass(Class cls, Class previously, int flags)
{
runtimeLock.assertLocked();
ASSERT((flags & ATTACH_CLASS) ||
(flags & ATTACH_METACLASS) ||
(flags & ATTACH_CLASS_AND_METACLASS));
auto &map = get();
auto it = map.find(previously);
if (it != map.end()) {
category_list &list = it->second;
if (flags & ATTACH_CLASS_AND_METACLASS) {
int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
// 實(shí)例方法
attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
// 類方法
attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
} else {
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
核心方法是attachCategories,添加分類信息,分為+和-方法。
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
if (slowpath(PrintReplacedMethods)) {
printReplacements(cls, cats_list, cats_count);
}
if (slowpath(PrintConnecting)) {
_objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
}
/*
* Only a few classes have more than 64 categories during launch.
* This uses a little stack, and avoids malloc.
*
* Categories must be added in the proper order, which is back
* to front. To do that with the chunking, we iterate cats_list
* from front to back, build up the local buffers backwards,
* and call attachLists on the chunks. attachLists prepends the
* lists, so the final result is in the expected order.
*/
constexpr uint32_t ATTACH_BUFSIZ = 64;
// 二維數(shù)組
method_list_t *mlists[ATTACH_BUFSIZ];
property_list_t *proplists[ATTACH_BUFSIZ];
protocol_list_t *protolists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
uint32_t propcount = 0;
uint32_t protocount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
// ?? 如果rew沒(méi)有,初始化rwe 并把ro中的方法、屬性、協(xié)議列表儲(chǔ)存到rwe中
auto rwe = cls->data()->extAllocIfNeeded();
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle); //排序
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
//倒敘插入
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
??auto rwe = cls->data()->extAllocIfNeeded(),rwe是在這個(gè)方法初始化的,因?yàn)檫@里要對(duì)ro的clean memory進(jìn)行處理了,并把ro中的方法、屬性、協(xié)議列表賦值到rwe中,全局搜索cls->data()->extAllocIfNeeded()發(fā)現(xiàn)除了這里還有_class_addProperty、class_addProtocol、addMethods三個(gè)方法進(jìn)行調(diào)用,原理相同就是要改變ro本來(lái)的clean memory的時(shí)候才會(huì)開(kāi)辟。
attachLists方法
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
// 舊數(shù)組容量
uint32_t oldCount = array()->count;
// 新數(shù)組容量
uint32_t newCount = oldCount + addedCount;
// 開(kāi)辟新數(shù)組
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 向后移動(dòng)要加入的數(shù)量位
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
//memcpy 從什么位置開(kāi)始拷貝什么 放多大
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;
// oldlist放最后
if (oldList) array()->lists[addedCount] = oldList;
//memcpy 從開(kāi)始位置開(kāi)始放入addedLists,
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
attachLists方法主要是為methods、properties、protocols二維數(shù)組賦值.
-
list = addedLists[0] 為
0-1過(guò)程。 -
1 list -> many lists 為
1-多過(guò)程,先將舊數(shù)組放到最后位置,然后從位置0插入新數(shù)據(jù)。 -
many lists -> many lists 為多對(duì)多過(guò)程,先將舊數(shù)組后移
要添加的數(shù)量位置,然后從位置0插入新數(shù)據(jù)。

分類加載
1.定義一個(gè)類,兩個(gè)分類,有同名方法
@interface Person : NSObject
-(void)say;
@end
@implementation Person
- (void)say
{
}
//+ (void)load
//{
// NSLog(@" %s", __func__);
//}
//////////////////////////////////////////
@interface Person (e1)
-(void)addmethod2;
-(void)addmethod1;
@end
-(void)addmethod2
{
}
-(void)addmethod1
{
}
//
//+ (void)load
//{
// NSLog(@" %s", __func__);
//}
-(void)say
{
NSLog(@"e1 %s", __func__);
}
//////////////////////////////////////////
@interface Person (e2)
-(void)aaaaaaa;
@end
@implementation Person (e2)
//+ (void)load
//{
// NSLog(@" %s", __func__);
//// method_exchangeImplementations(class_getInstanceMethod(self, @selector(say)), class_getInstanceMethod(self, @selector(say2)));
//}
-(void)aaaaaaa
{
}
-(void)say
{
NSLog(@"e2 %s", __func__);
}
main函數(shù)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *p =[Person alloc];
[p say];
}
return 0;
}
2.運(yùn)行查看堆棧信息。
-
1.都不實(shí)現(xiàn)load方法
分類分析1.png
從打印堆棧順序可知,先執(zhí)行的 main.m中的[p say],開(kāi)始方法的慢速查找,realizeClassWithoutSwift實(shí)現(xiàn)該類,ro->baseMethods()取出的list已經(jīng)包括了所有的方法,來(lái)打印下:

包括聲明的所有方法,但是是沒(méi)有順序的。往下一步排序:

已經(jīng)是排序好的方法。
-
2.主類實(shí)現(xiàn)load方法,分類不實(shí)現(xiàn)
分類分析2.png
從堆棧調(diào)用信息可知,此流程通過(guò)map_images->_read_images實(shí)現(xiàn)類,在main方法之前調(diào)用,通過(guò)剛才總結(jié)的_read_images的第九步實(shí)現(xiàn)類。

`ro->baseMethods()`取出的`list`已經(jīng)包括了所有的方法,后續(xù)步驟與第一步相同,只是前面執(zhí)行的時(shí)機(jī)不相同。
-
3.主類實(shí)現(xiàn)load方法,分類實(shí)現(xiàn)load方法
分類分析3.png
分類分析3.1.png
通過(guò)方法打印可知先調(diào)用的map_images->_read_images實(shí)現(xiàn)類,然后通過(guò)load_images->loadAllCategories-...-->attachCategories添加分類數(shù)據(jù),在此時(shí)創(chuàng)建rwe,把分類方法經(jīng)過(guò)排序后放進(jìn)rwe->methods中,attachLists方法走一對(duì)多過(guò)程。 -
4.主類不實(shí)現(xiàn)load方法,分類實(shí)現(xiàn)load方法
分類分析4.png
通過(guò)堆棧打印信息可知,通過(guò)load_images->prepare_load_methods->realizeClassWithoutSwift先實(shí)現(xiàn)本類。

通過(guò)unattachedCategories.attachToClass->attachCategories添加分類方法,rwe在attachCategories方法中創(chuàng)建,attachLists添加分類方法走1 list -> many lists過(guò)程。




