前言
iOS 底層第15
天的學(xué)習(xí)。在第14
天的學(xué)習(xí)中补疑,已經(jīng)分析了 readClass
,而 ro,rw
是在何時(shí)進(jìn)行賦值我們還不清楚歧沪,接下來(lái)繼續(xù)進(jìn)行探索。
read_images 探索
- 在
read_images
里繼續(xù)尋找有關(guān)class
代碼
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
//... 省略部分代碼
// 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;
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");
//... 省略部分代碼
}
- 靜態(tài)分析發(fā)現(xiàn)了在
non-lazy classes
有關(guān)于class
的處理癣丧, - 找到核心代碼后加入
XKStudent
進(jìn)行普通類攔截 - 在分析前查看注解
(for +load methods and static instances)
后發(fā)現(xiàn)要先在XKStudent
類里實(shí)現(xiàn)load
方法才會(huì)調(diào)用槽畔。 - 加入
load
方法進(jìn)行動(dòng)態(tài)分析, 來(lái)到了realizeClassWithoutSwift
- 進(jìn)入
realizeClassWithoutSwift
,開始動(dòng)態(tài)分析XKStudent
類
- 打印輸出??
- 由輸出的
methods_list count = 3
我們得知在進(jìn)行ro
賦值時(shí),已經(jīng)把methods
給加入到ro
里胁编,我們繼續(xù)step
- 由??得知:復(fù)制了一份
ro
給rw
厢钧,繼續(xù)往下step
- 由?? 兩個(gè)代碼可知:印證了
類的繼承鏈圖
和isa走位圖
- 繼續(xù)
step
進(jìn)入methodizeClass
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();
// ...
// 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);
}
// ...
}
- 進(jìn)入
prepareMethodLists
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
// ...
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*/);
}
}
// ...
}
- 進(jìn)入
fixupMethodList
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));
printf(" name is %s - meth.name is %p \n",name,meth.name());
}
}
printf("----------------- Sort 后 ----------------- \n");
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf(" name is %s - meth.name is %p \n",name,meth.name());
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
- 我們 在
fixupMethodList
加入了兩段打印
,一個(gè)是在sort
前,一個(gè)在sort
后嬉橙,打印??
- 得出的結(jié)論
methods
的排列順序是按照指針地址
由小到大進(jìn)行排序 - 最后我們梳理一下在
realizeClassWithoutSwift
里做了哪些事情ro = cls->data()早直,ro的賦值
rw = ro ,ro 復(fù)制一份給rw
類的繼承鏈, isa走位圖初始化
basemethods的排序
這時(shí)你是否會(huì)有個(gè)疑問市框,在上面可知只有實(shí)現(xiàn)
load
方法才會(huì)調(diào)用read_images -> realizeClassWithoutSwift
,當(dāng)不實(shí)現(xiàn)load
方法時(shí)是怎么加載的呢霞扬?
load 探索
- 把
load
方法去掉,動(dòng)態(tài)運(yùn)行程序 - 我們發(fā)現(xiàn)沒有調(diào)用
read_images
,但還是會(huì)進(jìn)入到realizeClassWithoutSwift
這個(gè)方法里枫振,覺得很奇怪bt
一下
- 當(dāng)把
load
方法去掉喻圃,調(diào)用方法時(shí)候會(huì)發(fā)送消息進(jìn)行lookUpImpOrForward
進(jìn)行慢速查找,最終也是會(huì)來(lái)到realizeClassWithoutSwift
粪滤。這就是所謂的懶加載
只有當(dāng)方法調(diào)用的時(shí)候才會(huì)去做相應(yīng)的ro斧拍,rw
處理。數(shù)據(jù)加載推遲到第一次消息的
時(shí)候杖小。 - 而
非懶加載
在map_images
的時(shí)候肆汹,加載所有類數(shù)據(jù)。 - 流程圖??
what is category
- 新建一個(gè)
category
,代碼??
@interface XKStudent (XK)
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
- (void) readBook1;
- (void) readBook2;
+ (void) readBook3;
@end
@implementation XKStudent (XK)
- (void) readBook1 {
NSLog(@"%s",__func__);
}
- (void) readBook2 {
NSLog(@"%s",__func__);
}
+ (void) readBook3 {
NSLog(@"%s",__func__);
}
@end
-
clang
一下
clang -rewrite-objc main.m -o main.cpp
- 查看
.cpp
文件代碼
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_XKStudent_$_XK,
};
// 在 _category_t 生成了 CATEGORY_XKStudent_$_XK
- 全局搜索
_category_t
struct _category_t {
const char *name; // 別名 = XK
struct _class_t *cls; // 類的引用
const struct _method_list_t *instance_methods; // 存儲(chǔ)實(shí)例方法
const struct _method_list_t *class_methods; // 存儲(chǔ)類方法
const struct _protocol_list_t *protocols; // 存儲(chǔ)協(xié)議
const struct _prop_list_t *properties; // 存儲(chǔ)屬性
};
- 全局搜索
_method_list_t
查看一下方法的定義
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_XKStudent_$_XK __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"readBook1", "v16@0:8", (void *)_I_XKStudent_XK_readBook1},
{(struct objc_selector *)"readBook2", "v16@0:8", (void *)_I_XKStudent_XK_readBook2}}
};
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_XKStudent_$_XK __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"readBook3", "v16@0:8", (void *)_C_XKStudent_XK_readBook3}}
};
- 可知在
編譯
時(shí) 有實(shí)例方法
和類方法
予权,卻沒有屬性get,set
昂勉,所以我們可以通過(guò)runtime
里關(guān)聯(lián)對(duì)象
進(jìn)行處理 - 接下來(lái)通過(guò)底層源碼來(lái)驗(yàn)證一下
category_t
內(nèi)部結(jié)構(gòu)
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
- 根據(jù)
_classProperties
注解驗(yàn)證了屬性
不是一直存在于disk
,而是通過(guò)運(yùn)行時(shí)
去添加
還有一點(diǎn)疑問為什么
category
的方法要將實(shí)例方法
和類方法
分開進(jìn)行定義呢扫腺?
- 我想最主要的原因就是
category
沒有元類
總結(jié)
- 今天我們從
read_images
來(lái)到了realizeClassWithoutSwift
岗照,在realizeClassWithoutSwift
做了對(duì)ro,rw
的賦值,以及類的繼承鏈,isa走位圖
的處理谴返; - 根據(jù)類
load
方法的實(shí)現(xiàn)與否還得知了類的加載有懶加載
和非懶懶加載
煞肾,它們之間的流程是完全不同的; - 最后還簡(jiǎn)單的分析了
category
的內(nèi)部結(jié)構(gòu) - 但
category
是如何加載到類
里,讓類
能夠調(diào)用其內(nèi)部的方法
的纬霞?我們還不清楚芋肠,期待下一次的分析。