iOS 底層學(xué)習(xí)15

前言

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ù)制了一份 rorw 厢钧,繼續(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)部的方法的纬霞?我們還不清楚芋肠,期待下一次的分析。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撵溃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梧却,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件败去,死亡現(xiàn)場(chǎng)離奇詭異放航,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)圆裕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門广鳍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吓妆,你說(shuō)我怎么就攤上這事赊时。” “怎么了行拢?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵祖秒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我舟奠,道長(zhǎng)竭缝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任沼瘫,我火速辦了婚禮抬纸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晕鹊。我一直安慰自己松却,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布溅话。 她就那樣靜靜地躺著晓锻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪飞几。 梳的紋絲不亂的頭發(fā)上砚哆,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音屑墨,去河邊找鬼躁锁。 笑死纷铣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的战转。 我是一名探鬼主播搜立,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼槐秧!你這毒婦竟也來(lái)了啄踊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刁标,失蹤者是張志新(化名)和其女友劉穎颠通,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膀懈,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顿锰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了启搂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硼控。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖狐血,靈堂內(nèi)的尸體忽然破棺而出淀歇,到底是詐尸還是另有隱情,我是刑警寧澤匈织,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布浪默,位于F島的核電站,受9級(jí)特大地震影響缀匕,放射性物質(zhì)發(fā)生泄漏纳决。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一乡小、第九天 我趴在偏房一處隱蔽的房頂上張望阔加。 院中可真熱鬧,春花似錦满钟、人聲如沸胜榔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)夭织。三九已至,卻和暖如春吠撮,著一層夾襖步出監(jiān)牢的瞬間尊惰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弄屡,地道東北人题禀。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像膀捷,于是被迫代替她去往敵國(guó)和親迈嘹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容