iOS Runtime學(xué)習(xí)(二) -- Runtime執(zhí)行順序

一颇象、什么是Runtime?

OC是一門動態(tài)類型的語言,它允許很多操作都推遲到程序運行時再進行
OC的動態(tài)性就是由Runtime來支撐和實現(xiàn)的并徘,而Runtime是一套C語言的API遣钳,它封裝了很多動態(tài)性相關(guān)的函數(shù)
我們平時編寫的OC代碼,其實底層都是將代碼轉(zhuǎn)換成了Runtime API來進行調(diào)用

二麦乞、OC的消息機制

OC的方法調(diào)用其實都是轉(zhuǎn)成了objc_msgSend函數(shù)的調(diào)用蕴茴,給方法的調(diào)用者發(fā)送了一條消息。
objc_msgSend底層有3個階段

  • 消息發(fā)送(當(dāng)前類姐直、父類中查找)
  • 動態(tài)方法解析
  • 消息轉(zhuǎn)發(fā)

三倦淀、流程解析

例如,我們有一個Person類声畏,將這個Person實例化對象
先導(dǎo)入頭文件

#import <objc/runtime.h>
#import <objc/message.h>
#import "Person.h"
//oc寫法
Person *person1 = [[Person alloc] init];
//runtime寫法
Person *person2 = objc_msgSend(objc_msgSend([Person class], @selector(alloc)), @selector(init));

新工程這里會報錯Too many arguments to function cal
這個問題只需要在FuDemo->Target中Build Setting的Enable Strict Checking of objc_msgSend Calls的值設(shè)置為NO即可撞叽。

運行程序,我們能看到砰识,person1與person2都創(chuàng)建成功了,接下來就看看匯編代碼片段是不是執(zhí)行了objc_msgSend方法能扒。首先將Person1的初始化代碼注釋掉,然后打開Always Show Disassembly辫狼,讓我們在調(diào)試時初斑,斷點能直接進入到匯編代碼界面,如下圖:

1.png
最后將斷點打在Person初始化那一行膨处,Command+R见秤。
從匯編代碼界面,我們能看到如下信息:
Person進行了alloc真椿,然后該對象調(diào)用了objc_msgSend進行init鹃答。
將斷點執(zhí)行到調(diào)用objc_msgSend方法,‘按住Control + step into’查看objc_msgSend內(nèi)部實現(xiàn)突硝,再用同樣方法查看objc_msgSend_uncached测摔,最終我們可以找到class_lookupMethodAndLoadCache3這樣的調(diào)用步驟。

四解恰、objc_msgSend執(zhí)行流程

工程搜索objc_msgSend锋八,找到objc-msg-x86_64.s我們可以找到如下代碼片段

/********************************************************************
 *
 * id objc_msgSend(id self, SEL _cmd,...);
 * IMP objc_msgLookup(id self, SEL _cmd, ...);
 *
 * objc_msgLookup ABI:
 * IMP returned in r11
 * Forwarding returned in Z flag
 * r10 reserved for our use but not used
 *
 ********************************************************************/

這下面就是實現(xiàn)的主要步驟,我們抽取主要信息查看

    /*進入到objc_msgSend*/
    ENTRY _objc_msgSend             
    
    /*查找當(dāng)前isa*/
    GetIsaFast NORMAL       // r10 = self->isa
    /*從緩存中查找當(dāng)前方法护盈,如果查找到了IMP將結(jié)果返回給調(diào)用者*/
    CacheLookup NORMAL, CALL    // calls IMP on success
/*在緩存中沒找到挟纱,搜索方法列表*/
// cache miss: go search the method lists
LCacheMiss:
    /*\*/
    // isa still in r10
    /*跳轉(zhuǎn)objc_msgSend_uncached*/
    jmp __objc_msgSend_uncached

objc_msgSend_uncached內(nèi)基本都是在調(diào)用MethodTableLookup(從當(dāng)前方法列表中查找)。如果找到腐宋,則將IMP放到寄存器中紊服。
MethodTableLookup能找到class_lookupMethodAndLoadCache3被調(diào)用檀轨,正好這也是之前我們所驗證的最后一部。
class_lookupMethodAndLoadCache3中只做了lookUpImpOrForward(查找方法實現(xiàn))這一件事
因此runtime的執(zhí)行順序為:

1.objc_msgSend
2.CacheLookup(有緩存IMP欺嗤,則返回給調(diào)用者参萄。沒有緩存則往下執(zhí)行)
3.objc_msgSend_unCached
4.MethodTableLookup
5.class_lookupMethodAndLoadCache3
6.lookUpImpOrForward

五、lookUpImpOrForward代碼片段注釋

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    /*從緩存中查找*/
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    /*確保當(dāng)前isa初始化*/
    checkIsKnownClass(cls);
    if (!cls->isRealized()) {
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
    }
 
 retry:    
    runtimeLock.assertLocked();

    /*從當(dāng)前類的緩存中查找*/
    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    /*從當(dāng)前類的方法列表中查找*/
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            /*將查找到的meth放入緩存中*/
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    /*沿著繼承鏈向上查找*/
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    break;
                }
            }
            
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    /*沒有找到IMP煎饼,嘗試動態(tài)決議 resolveInstanceMethod*/
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        triedResolver = YES;
        goto retry;
    }

    /*沒有實現(xiàn)動態(tài)決議方法拧揽,觸發(fā)消息轉(zhuǎn)發(fā)流程 (forwardingTargetForSelector和forwardInvocation)*/
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腺占,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痒谴,老刑警劉巖衰伯,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異积蔚,居然都是意外死亡意鲸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門尽爆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怎顾,“玉大人,你說我怎么就攤上這事漱贱』蔽恚” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵幅狮,是天一觀的道長募强。 經(jīng)常有香客問我,道長崇摄,這世上最難降的妖魔是什么擎值? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮逐抑,結(jié)果婚禮上鸠儿,老公的妹妹穿的比我還像新娘。我一直安慰自己厕氨,他們只是感情好进每,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著腐巢,像睡著了一般品追。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冯丙,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天肉瓦,我揣著相機與錄音遭京,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛绊困,可吹牛的內(nèi)容都是我干的茁影。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼斯嚎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挨厚?” 一聲冷哼從身側(cè)響起堡僻,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疫剃,沒想到半個月后钉疫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巢价,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年牲阁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壤躲。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡城菊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碉克,到底是詐尸還是另有隱情凌唬,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布棉胀,位于F島的核電站法瑟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唁奢。R本人自食惡果不足惜霎挟,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望麻掸。 院中可真熱鬧酥夭,春花似錦、人聲如沸脊奋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诚隙。三九已至讶隐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間久又,已是汗流浹背巫延。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工效五, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炉峰。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓畏妖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疼阔。 傳聞我的和親對象是個殘疾皇子戒劫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評論 0 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,089評論 1 32
  • 前言 runtime其實在我們?nèi)粘i_發(fā)過程中很少使用到婆廊,尤其是像我現(xiàn)在比較初級的程序猿就更用不到了迅细。但是去面試很多...
    WolfTin閱讀 611評論 0 2
  • 他們都想去島上碰碰運氣,湯尼和九只小蝙蜥留在了咕嚕船上淘邻。 安德滿在海灘上摸清了沙子的溫度是正常的疯攒。 他們走過了白色...
    天修極樂閱讀 414評論 0 0
  • 我有一份清閑的工作,閑暇時間幫著男友打理生意列荔,開一個小店就像一個小小的創(chuàng)業(yè),需要資金運轉(zhuǎn)枚尼,需要有效的盈利模式贴浙,需要...
    Stone_blossom閱讀 362評論 0 1