接續(xù)上一篇文章類加載原理(上)的內(nèi)容我們繼續(xù)探索妇汗。上一篇我們一直在尋找關于類Class
的一些處理比如加載ro\rw
跋破。所以我們在_read_images
方法里把代碼根據(jù)不同功能區(qū)分了十個部分。并且根據(jù)我們的目的利用特殊打印排除的方法查找了幾個火脉。下面我們繼續(xù)查找關于類的代碼部分。
類的加載處理:
非懶加載類加載處理流程
我們同樣利用特殊打印排除的方法來看看我們的類ZYPerson
是否會進入到這里。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
/*
* 省略上面的代碼
*/
// 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)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
/* *********************嘗試打印看看我們自己的類能不能進來*****************/
const char *mangledName = cls->nonlazyMangledName();
//定義自己的類名
const char *ZYPersonName = "ZYPerson";
//比較自己的類名和讀取的是否一致一致就進入if
if (strcasecmp(ZYPersonName, mangledName) == 0) {
printf("Realize non-lazy classes - ZY 我們需要跟蹤的信息: %s - - %s\n",__func__,mangledName);
}
/* *********************嘗試打印看看我們自己的類能不能進來*****************/
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);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
/*
* 省略下面的代碼
*/
}
我們嘗試跑起了我們的代碼曙蒸,發(fā)現(xiàn)并沒有直接進來我們的打印打碼润绵,并且我嘗試利用斷點來跟蹤也發(fā)現(xiàn)并沒有進這個方法线椰。但是這個方法明明是對類的加載的一些處理呀(看注釋)最后我發(fā)現(xiàn)注釋里有解釋那就是這段代碼是對非懶加載
類的處理要走這里就先要使我們的類成為非懶加載
的類,for +load methods and static instances
所以我在我的ZYPerson
類里實現(xiàn)了一下+ (void *)load{}
方法使之成為非懶加載
類尘盼。然后來跑代碼憨愉,誒?發(fā)現(xiàn)真的進來了卿捎。如下:
ZYPerson.h
:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ZYPerson : NSObject
- (void)zyEatSugar;
+ (void)sayHappy;
- (void)zySay1;
- (void)zySay2;
- (void)zySay3;
- (void)zyShowTime;
@end
NS_ASSUME_NONNULL_END
ZYPerson.m
:
#import "ZYPerson.h"
@implementation ZYPerson
+(void)load{
}
- (void)zyEatSugar
{
NSLog(@"%s",__func__);
}
+ (void)sayHappy
{
NSLog(@"%s",__func__);
}
- (void)zySay1
{
NSLog(@"%s",__func__);
}
- (void)zySay2
{
NSLog(@"%s",__func__);
}
- (void)zySay3
{
NSLog(@"%s",__func__);
}
- (void)zyShowTime
{
NSLog(@"%s",__func__);
}
main.m
:
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZYPerson *person = [ZYPerson alloc];
//調(diào)用ZYPerson 的zyEatSugar 方法 配紫,該方法未實現(xiàn)
[person zyEatSugar];
}
return 0;
}
打印結(jié)果:
Realize non-lazy classes - ZY 我們需要跟蹤的信息: _read_images - - ZYPerson
KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
2021-08-03 14:37:38.175895+0800 KCObjcBuild[25482:4921796] -[ZYPerson zyEatSugar]
Program ended with exit code: 0
發(fā)現(xiàn)是走了我們的打印的。所以我們再次回到這段代碼午阵。我們發(fā)現(xiàn)這段代碼其實就兩個方法是我們需要關注的其他的都是一些判斷和數(shù)據(jù)處理笨蚁。一個是addClassTableEntry(cls);
這個方法我們在上一篇文章——類加載原理(上)中的readClass
方法里去探索過,發(fā)現(xiàn)只是對類的名字/mangledName
和地址
以HashMap
的形式添加進表
的動作趟庄。通過這個方法我們只能把類名和地址關聯(lián)起來括细,但是它怎么實現(xiàn)的還沒看到。所以我們看另一個方法:realizeClassWithoutSwift(cls, nil);
1,realizeClassWithoutSwift(cls, nil);
:
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// 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->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 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.
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls, previously);
return cls;
}
這個方法我們先在開頭加入我們的特殊打印打碼戚啥,OB
一下奋单,看看是否會進來,若是進來猫十,并且我們看到這里有ro
的出現(xiàn)览濒。我們就查看下下面的ro
是否存在。
從上面的操作截圖來看拖云,ZYPerson
確實進來了這個方法贷笛,但是直接打印ro
里的方法列表卻打印不出來。而從上面我們貼出來的代碼看我們的ZYPerson
里是有方法的宙项。我們在main
函數(shù)里還調(diào)用了乏苦。所以到這里我們的方法還沒有加載進來。我們接著往下找尤筐。
我們在下面的代碼繼續(xù)添加斷點來跟蹤下如下圖:
我們發(fā)現(xiàn)ZYPerson
走了eles
而我們在eles
里看到了我們熟悉并且一直尋找的字眼ro
汇荐、rw
。我們在上面看到這里的ro
是從cls->data()
里獲取的盆繁。而這里是又利用ro
進行處理賦值給我rw
掀淘。我們就回顧去看看這個ro到底怎么獲取的。
auto ro = (const class_ro_t *)cls->data();
class_rw_t *data() const {
return bits.data();
}
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
看上面我們可以知道原來
ro
是直接讀取出來并且rw
應該是包含ro
的數(shù)據(jù)格式的油昂,因為他是以rw
格式強轉(zhuǎn)出來的乳蓄。
在上面的
eles
代碼我們還看到了將讀取出來的ro
直接利用set
方法set
進了一個新創(chuàng)建的rw
里。然后判斷是否是元類來設置rw
的flags
。最后將rw
利用set
方法set
進了class
的data()``里。這就是類加載的時候
ro具壮、
rw`的處理。
我們接著往下看:
下面就是對類的cache做初始化處理:
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
然后就是獲取到本類的父類和元類:
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 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.
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
然后就是判斷是否是符合SUPPORT_NONPOINTER_ISA
如果符合就走
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
在這里判斷是否是元類,分別對類做處理。然后就到了下面的代碼:
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
在這里就根據(jù)前面獲取的
父類
、元類
然后把本類
和元類
以及父類
都做了相應的關聯(lián)
處理我衬。這里就是我們常用到的isa走位圖
的實現(xiàn)和繼承鏈
的實現(xiàn)了叹放。
最后我們看到還剩一行代碼:
// Attach categories
methodizeClass(cls, previously);
return cls;
2,methodizeClass
:
在這行代碼我們看名稱是對方法的處理挠羔。我們剛在前面利用ro打印方法列表的時候并未打印出來井仰。因為那個時候的類還只有一個名稱和一個地址關聯(lián),并沒有方法ro破加、rw的處理俱恶。但是到這行代碼我們不禁想,經(jīng)過上面的對類的一些列處理范舀,這里是否有了方法呢合是?下面我們進入這個方法并且利用我們的特殊打印來打印一下
/***********************************************************************
* 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();
/* *********************嘗試打印看看我們自己的類能不能進來*****************/
const char *mangledName = cls->nonlazyMangledName();
//定義自己的類名
const char *ZYPersonName = "ZYPerson";
//定義變量來存儲本次進來的是否是元類
auto zy_ro = (const class_ro_t *)cls->data();
auto zy_isMeta = zy_ro->flags & RO_META;
//比較自己的類名和讀取的是否一致一致就進入if
if (strcasecmp(ZYPersonName, mangledName) == 0) {
if (!zy_isMeta) {//判斷不是元類就打印
printf("realizeClassWithoutSwift---2222 -- ZY 我們需要跟蹤的信息: %s - - %s\n",__func__,mangledName);
}
}
/* *********************嘗試打印看看我們自己的類能不能進來*****************/
/*
* 省略下面的代碼
*/
我們排除元類后就直接打印斷點打印代碼,然后去lldb
調(diào)試查看是否有方法锭环。
Realize non-lazy classes - ZY 我們需要跟蹤的信息: _read_images - - ZYPerson
realizeClassWithoutSwift - ZY 我們需要跟蹤的信息: realizeClassWithoutSwift - - ZYPerson
realizeClassWithoutSwift - ZY 我們需要跟蹤的信息: realizeClassWithoutSwift - - ZYPerson
(lldb) p ro
(const class_ro_t *) $0 = 0x00000001000081c8
(lldb) p *40
error: <user expression 1>:1:1: indirection requires pointer operand ('int' invalid)
*40
^~~
(lldb) p *$0
(const class_ro_t) $1 = {
flags = 0
instanceStart = 8
instanceSize = 40
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "ZYPerson" {
Value = 0x0000000100003f07 "ZYPerson"
}
}
baseMethodList = 0x0000000100008210
baseProtocols = nil
ivars = 0x0000000100008290
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008318
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $1->baseMethodList
(void *const) $2 = 0x0000000100008210
Fix-it applied, fixed expression was:
$1.baseMethodList
(lldb) p *$2
(lldb) p *$2
(lldb)
從上面的lldb
調(diào)試發(fā)現(xiàn)到這個方法開始ro
里都沒有方法列表聪全。所以我們接著往下看。
在這個方法里我們打印的下方有對方法的處理
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
這里直接去獲取baseMethods
如果不為空就去做一些預備的處理進入方法prepareMethodLists
辅辩。所以我們跟蹤到這個處理方法看看
3难礼,prepareMethodLists
:
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
// There exist RR/AWZ/Core special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ/Core:
// default RR/AWZ/Core cannot be set before setInitialized().
// Therefore we need not handle any special cases here.
if (baseMethods) {
ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
} else if (cls->cache.isConstantOptimizedCache()) {
cls->setDisallowPreoptCachesRecursively(why);
} else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
cls->setDisallowPreoptInlinedSelsRecursively(why);
}
#endif
}
// Add method lists to array.
// Reallocate un-fixed method lists.
// The new methods are PREPENDED to the method list array.
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[I];
ASSERT(mlist);
// Fixup selectors if necessary
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
// If the class is initialized, then scan for method implementations
// tracked by the class's flags. If it's not initialized yet,
// then objc_class::setInitialized() will take care of it.
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}
在這個方法我們看到前面有一些判斷和處理 但是重點是在于中間的for循環(huán)
。我們在for循環(huán)
里打個斷點然后進行lldb
調(diào)試看看到底是怎么處理和進入的玫锋。如圖5
經(jīng)過上面斷點調(diào)試和輸出我們發(fā)現(xiàn)確實進入了這個for循環(huán)并且進入了方法
fixupMethodList
進行了方法修復蛾茉。我們就跟蹤到這個方法去看看他的實現(xiàn):
4,fixupMethodList
:
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
// Sort by selector address.
// Don't try to sort small lists, as they're immutable.
// Don't try to sort big lists of nonstandard size, as stable_sort
// won't copy the entries properly.
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
方法分析:
第一步:是進行了for循環(huán)
的遍歷去獲取到了每一個方法的名字然后利用注冊方法sel_registerNameNoLock
進行了名字sel
綁定注冊最后利用set
方法set
進了method
撩鹿。
第二步:進行了sort
排序谦炬。根據(jù)注釋我們知道他不會對small lists
方法進行排序因為他不可變。
第三步:給非small lists
方法列表打標記調(diào)用setFixedUp
方法节沦。
我們不妨來打印下吧寺,看看排序前和排序后的方法名稱和地址。同時為了控制使我們自己的類的方法我們在上面的methodizeClass
方法添加的打印里打上一個斷點散劫,只有來到這個斷點的時候證明是我們自己的類ZYPerson
的方法稚机。這個時候我們再清空打印,并且在for循環(huán)打印的代碼后面加上一個斷點(此斷點隨意打在打印后的代碼就可)為了避免系統(tǒng)后面的方法也打印出來获搏。
讓我們要觀察的方法打印出來赖条。如圖6:
打印結(jié)果:如圖7
到這里我們prepareMethodLists
方法的主要流程就走完了失乾。
下面我們回到prepareMethodLists
方法入口methodizeClass
方法里。在下一行代碼打上斷點查看走向纬乍。如圖8
我們看到rwe
在這個方法里為NULL
碱茁。所以不會走下面的if
方法attachLists
。那么這個rwe
是什么時候才會賦值的呢仿贬?我們放到文章后面來探討纽竣。
至此我們可以理一遍流程:
第一步:從_read_images
方法進入 查找到關于類的方法readClass
進行了類的名字和類的地址綁定;
第二步:走到另一部分代碼 進行非懶加載類的加載處理 然后進入方法 realizeClassWithoutSwift
進行ro
茧泪、rw
處理和父類
蜓氨、元類
、isa走位
綁定處理队伟。然后進入方法處理方法methodizeClass
;
第三步:在這個方法進行方法
穴吹、屬性
、分類
的處理嗜侮,我們只是跟蹤到了方法處理港令,進入到了方法預處理方法prepareMethodLists
第四步:對方法進行遍歷然后調(diào)用方法修復方法fixupMethodList
第五步:fixupMethodList
方法對方法進行名字
和sel
綁定,并且排序處理锈颗。
這就是上面我們探索的內(nèi)容顷霹。
總結(jié):回歸到我們之前為我們的類添加load
方法,我們不加load
方法是不會走我們read_images
里的非懶加載類的處理流程击吱。蘋果這樣做的目的就是為了節(jié)約內(nèi)存
泼返,為了使得在開發(fā)過程中的類得到區(qū)分,把一些類定義為懶加載類(沒有實現(xiàn)load
方法的)姨拥,在這些懶加載類沒有用到之前都不會去加載和處理绅喉。這樣就會使得內(nèi)存得到大量的結(jié)余。啟動時間更短運行更快叫乌。
疑問:既然上面我們談到非懶加載類會走上面的處理流程柴罐,那懶加載類呢?懶加載類怎么處理的呢憨奸?
懶加載類加載處理流程
我們從上面非懶加載類的處理流程從read_images里的非懶加載類判斷入口進入到realizeClassWithoutSwift
方法革屠。之后進行一些列的類處理。那我們猜測是否在其他的地方也有有入口進入realizeClassWithoutSwift
這個方法呢排宰?我們就在這個方法里之前添加的特殊打印的地方加一個斷點似芝。并且把ZYPerson
的load
方法屏蔽掉.如果真的來了,那我們就利用bt
命令來查看堆棧信息板甘,追溯流程党瓮。
從堆棧流程我們發(fā)現(xiàn)是:
lookUpImpOrForward
->realizeAndInitializeIfNeeded_locked
->initializeAndLeaveLocked
->initializeAndMaybeRelock
->realizeClassMaybeSwiftAndUnlock
->realizeClassMaybeSwiftMaybeRelock
->realizeClassWithoutSwift
從這個流程進入了我們上面分析的類加載處理流程。
那么是什么時機調(diào)用的上面的流程呢盐类?
分析:我們從上面可以知道寞奸,當進入上面bt
流程的時候ZYPerson
只是進行了alloc
呛谜,并沒有去調(diào)用下面的方法。這時候就已經(jīng)進入到這里了枪萄。
結(jié)論:所以我們可以確定懶加載類是在第一次消息發(fā)送的時候就會去加載這個相關的類隐岛。
總結(jié):
下面我用兩張圖片來總結(jié)歸納懶加載和非懶加載類的加載處理過程:
分類(categofies
)提前預告:
我們在上面探索類的加載流程的時候在methodizeClass
方法里根據(jù)斷點跟蹤看到預處理方法列表后有判斷rwe是否存在,那個時候我們發(fā)現(xiàn)是不存在的瓷翻,那影響這個的因素是什么呢聚凹?而且我們在進入methodizeClass
方法前看到他的注釋是Attach categories
這不得不讓我們引發(fā)思考就是這里面的某些東西是否是跟分類有關的呢?下面我們來到main.m
文件在main
函數(shù)上方添加一個分類ZYPerson (ZY)
給這個類設置兩個屬性和三個方法齐帚。然后我們利用 clang -rewrite-objc main.m -o main.cpp
命令將main.m
文件轉(zhuǎn)成c++
文件看看其真實存在是什么樣的妒牙。
main.m
@interface ZYPerson (ZY)
@property (nonatomic, copy) NSString *zy_name;
@property (nonatomic, assign) int zy_age;
- (void)zy_instanceMethod1;
- (void)zy_instanceMethod2;
+ (void)zy_classMethod3;
@end
@implementation ZYPerson (ZY)
- (void)zy_instanceMethod1
{
NSLog(@"%s",__func__);
}
- (void)zy_instanceMethod2
{
NSLog(@"%s",__func__);
}
+ (void)zy_classMethod3
{
NSLog(@"%s",__func__);
}
@end
main.cpp
:
我們直接command+下
來到最后一行代碼,然后我們可以看到這樣的一行代碼:
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_ZYPerson_$_ZY,
};
在這里我們看到_CATEGORY_ZYPerson_
拼接一個_ZY
,就是我們自己創(chuàng)建的那個ZYPerson(ZY)
的分類童谒。我們搜索下_category_t
這個結(jié)構(gòu)體
_category_t
:
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
在_category_t結(jié)構(gòu)體的成員變量里看到了一個名字
name
,應該就是存儲ZY
這個字符;還有class
沪羔,應該就是ZYPerson
類饥伊;然后就是實例方法
、類方法
蔫饰、協(xié)議
和屬性
琅豆。
在這里我們發(fā)現(xiàn)類方法居然是這些實例方法放一起的。不過我們想到分類是沒有元類這一點也就能夠理解了篓吁。因為沒有元類所以他的類方法只能直接和實例方法放在一起了茫因。
我們繼續(xù)搜索下_category_t
分類的東西
static struct _category_t _OBJC_$_CATEGORY_ZYPerson_$_ZY __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"ZYPerson",
0, // &OBJC_CLASS_$_ZYPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_ZYPerson_$_ZY,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_ZYPerson_$_ZY,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_ZYPerson_$_ZY,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_ZYPerson_$_ZY,
};
在這個我們自己分類ZYPerson(ZY)的結(jié)構(gòu)體中看到有一個名字記錄的居然是
ZYPerson
?然后class
記錄的居然是個0
?其實這里應該只是做一個占位作用杖剪,因為class
不可能為0
冻押,而那個name
也不應該是ZYPerson
而應該是ZY
,唯一的解釋就是在編譯階段還不能確定這個名字和類盛嘿。所以只是占位洛巢。然后看到有實例方法
、類方法
次兆、協(xié)議
和屬性
稿茉,和上面的結(jié)構(gòu)體模型結(jié)構(gòu)是一致的。
我們繼續(xù)查看下關于分類的東西
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_ZYPerson_$_ZY __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"zy_instanceMethod1", "v16@0:8", (void *)_I_ZYPerson_ZY_zy_instanceMethod1},
{(struct objc_selector *)"zy_instanceMethod2", "v16@0:8", (void *)_I_ZYPerson_ZY_zy_instanceMethod2}}
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_ZYPerson_$_ZY __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"zy_classMethod3", "v16@0:8", (void *)_C_ZYPerson_ZY_zy_classMethod3}}
};
從上面的兩段代碼我們可以發(fā)現(xiàn)芥炭,雖然分類的實例方法和類方法放在一起漓库,但是他們在實現(xiàn)的時候是分開的。
協(xié)議_protocol_t
:
struct _protocol_t _OBJC_PROTOCOL_NSObject __attribute__ ((used)) = {
0,
"NSObject",
0,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSObject,
0,
(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_NSObject,
0,
(const struct _prop_list_t *)&_OBJC_PROTOCOL_PROPERTIES_NSObject,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSObject
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSObject = &_OBJC_PROTOCOL_NSObject;
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_ZYPerson_$_ZY __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSObject
};
屬性_prop_list_t
:
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_ZYPerson_$_ZY __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"zy_name","T@\"NSString\",C,N"},
{"zy_age","Ti,N"}}
};
在查找屬性的時候我們并沒有發(fā)現(xiàn)他的
setter/getter
方法园蝠。這也從側(cè)面反映了我們經(jīng)常提到的分類不能設置屬性的說法渺蒿,是因為它不會生成setter/getter
方法,所以我們才用關聯(lián)對象的方法來實現(xiàn)彪薛。
我們發(fā)現(xiàn)分類元類在編譯階段就已經(jīng)做了一些處理蘸嘶,尤其在類方法良瞧、屬性等特殊點上做了特殊的處理。所以我們下一篇文章就一起去探索下分類的一些實現(xiàn)原理训唱。
至此褥蚯,文章告一段落,原創(chuàng)碼字不易况增,如能給您帶來些許啟發(fā)那也是給作者的極大鼓勵赞庶。也盼望需要轉(zhuǎn)載的朋友請標注出處,謝謝澳骤!
遇事不決歧强,可問春風。站在巨人的肩膀上學習为肮,如有疏忽或者錯誤的地方還請多多指教摊册。