iOS runtime整理

runtime簡(jiǎn)介

Runtime 又叫運(yùn)行時(shí),是一套底層的 C 語(yǔ)言 API砰碴,是 iOS 系統(tǒng)的核心之一兽埃。開(kāi)發(fā)者在編碼過(guò)程中,可以給任意一個(gè)對(duì)象發(fā)送消息涡相,在編譯階段只是確定了要向接收者發(fā)送這條消息哲泊,而接受者將要如何響應(yīng)和處理這條消息,那就要看運(yùn)行時(shí)來(lái)決定了催蝗。 C語(yǔ)言中攻旦,在編譯期,函數(shù)的調(diào)用就會(huì)決定調(diào)用哪個(gè)函數(shù)生逸。 而OC的函數(shù),屬于動(dòng)態(tài)調(diào)用過(guò)程且预,在編譯期并不能決定真正調(diào)用哪個(gè)函數(shù)槽袄,只有在真正運(yùn)行時(shí)才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。

typedef struct objc_class *Class;
typedef struct objc_object *id;

@interface Object { 
    Class isa; 
}

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

struct objc_object {
private:
    isa_t isa;
}

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
}
  • Objective-C 對(duì)象都是 C 語(yǔ)言結(jié)構(gòu)體實(shí)現(xiàn)的锋谐,所有的對(duì)象都會(huì)包含一個(gè)isa_t類型的結(jié)構(gòu)體
  • objc_class繼承于objc_object遍尺。所以在objc_class中也會(huì)包含isa_t類型的結(jié)構(gòu)體isa。至此涮拗,可以得出結(jié)論:Objective-C 中類也是一個(gè)對(duì)象乾戏。
  • 我們可以認(rèn)為id中的isa指針指向的是一個(gè)類對(duì)象,并且在Class結(jié)構(gòu)體中的isa指針指向元類

對(duì)象的實(shí)例方法調(diào)用時(shí)三热,通過(guò)對(duì)象的 isa 在類中獲取方法的實(shí)現(xiàn)鼓择。
類對(duì)象的類方法調(diào)用時(shí),通過(guò)類的 isa 在元類中獲取方法的實(shí)現(xiàn)就漾。


對(duì)象呐能,類,元類之間的關(guān)系

SEL

typedef struct objc_selector *SEL;

objc_selector是一個(gè)映射到方法的C字符串抑堡。SEL是系統(tǒng)在編譯過(guò)程中摆出,會(huì)根據(jù)方法的名字以及參數(shù)序列生成一個(gè)用來(lái)區(qū)分這個(gè)方法的唯一ID編號(hào),這個(gè) ID 就是 SEL 類型的首妖。我們需要注意的是偎漫,不同類中相同名字的方法所對(duì)應(yīng)的方法選擇器是相同的,即使方法名字相同而變量類型不同也會(huì)導(dǎo)致它們具有相同的方法選擇器有缆。由于這點(diǎn)特性象踊,也導(dǎo)致了OC不支持函數(shù)重載温亲。
獲取SEL的幾種方法:

SEL aSel = @selector(didReceiveMemoryWarning);
SEL a_sel = NSSelectorFromString(@"didReceiveMemoryWarning");
SEL a_Sel = sel_registerName("didReceiveMemoryWarning");
NSLog(@"%p___%p___%p",aSel,a_sel,a_Sel);

Method

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;  //方法名
    char *method_types                                       OBJC2_UNAVAILABLE;  //參數(shù)類型以及返回值類型編碼
    IMP method_imp                                           OBJC2_UNAVAILABLE; //方法實(shí)現(xiàn)指針
}

獲取方法

// 獲取實(shí)例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

IMP

IMP即Implementation,是一個(gè)函數(shù)指針通危,指向的是函數(shù)的具體實(shí)現(xiàn)铸豁。在runtime中消息傳遞和轉(zhuǎn)發(fā)的目的就是為了找到IMP,并執(zhí)行函數(shù)菊碟。
獲取IMP的方法:

//通過(guò)Method獲取IMP
IMP method_getImplementation(Method m);
// 返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

runtime消息發(fā)送與轉(zhuǎn)發(fā)

消息發(fā)送和轉(zhuǎn)發(fā)
  • NilTest是用來(lái)檢測(cè)是否為nil的节芥,如果檢測(cè)方法的接受者是nil,那么系統(tǒng)會(huì)自動(dòng)clean并且return逆害。
  • Cache的作用主要是為了優(yōu)化方法調(diào)用的性能头镊。當(dāng)對(duì)象receiver調(diào)用方法message時(shí),首先根據(jù)對(duì)象receiver的isa指針查找到它對(duì)應(yīng)的類魄幕,然后在類的methodLists中搜索方法相艇,如果沒(méi)有找到,就使用super_class指針到父類中的methodLists查找纯陨,一旦找到就調(diào)用方法坛芽。如果沒(méi)有找到,有可能消息轉(zhuǎn)發(fā)翼抠,也可能忽略它咙轩。但這樣查找方式效率太低,因?yàn)橥粋€(gè)類大概只有20%的方法經(jīng)常被調(diào)用阴颖,占總調(diào)用次數(shù)的80%活喊。所以使用Cache來(lái)緩存經(jīng)常調(diào)用的方法,當(dāng)調(diào)用方法時(shí)量愧,優(yōu)先在Cache查找钾菊,如果沒(méi)有找到,再到methodLists查找偎肃。
  • MethodTableLookup 可以算是個(gè)接口層宏煞烫,主要用于保存環(huán)境與準(zhǔn)備參數(shù),來(lái)調(diào)用 __class_lookupMethodAndLoadCache3函數(shù)
  • __class_lookupMethodAndLoadCache3函數(shù)也是個(gè)接口層(C編寫(xiě))软棺,此函數(shù)提供相應(yīng)參數(shù)配置红竭,實(shí)際功能在lookUpImpOrForward函數(shù)中。
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    if (!cls->isRealized()) {
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        _class_initialize (_class_getNonMetaClass(cls, inst));
    }

 retry:    
    // Try this class's cache.
    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Stop searching, but don't cache yet; call method  resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        _class_resolveMethod(cls, sel, inst);
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help.  Use forwarding.
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

    return imp;
}
  • 調(diào)用realizeClass方法是申請(qǐng)class_rw_t的可讀寫(xiě)空間喘落。
  • _class_initialize是類初始化的過(guò)程茵宪。
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}
  • 這個(gè)函數(shù)首先判斷是否是meta-class類,如果不是元類瘦棋,就執(zhí)行_class_resolveInstanceMethod稀火,如果是元類,執(zhí)行_class_resolveClassMethod赌朋。
  • 為什么查找類方法的實(shí)現(xiàn)過(guò)程中會(huì)查看resolveInstanceMethod的實(shí)現(xiàn)凰狞。
    其根本原因?yàn)?NSObject 為root meta class類的根類篇裁,而root meta class 為所有其他meta class的 ISA指向的類。在類方法的巡查過(guò)程中赡若,通過(guò)meta class的繼承關(guān)系會(huì)最終找到NSObject類达布,所以在NSObject 的resolveInstanceMethod添加方法決議實(shí)現(xiàn)代碼,并且在類方法(+號(hào)方法)的決議過(guò)程中檢測(cè)該實(shí)現(xiàn)是合理的逾冬。
消息轉(zhuǎn)發(fā)

動(dòng)態(tài)方法解析
+resolveInstanceMethod:(實(shí)例方法)或者+resolveClassMethod:(類方法);我們有機(jī)會(huì)為該未知消息新增一個(gè)”處理方法””,不過(guò)使用該方法的前提是我們已經(jīng)實(shí)現(xiàn)了該”處理方法”

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString isEqualToString:@"methodO:"]) {
        Method addM = class_getInstanceMethod([self class], sel_registerName("functionMethodAddO:"));
        class_addMethod([self class], sel, method_getImplementation(addM), method_getTypeEncoding(addM));
    }
    return [super resolveInstanceMethod:sel];
}

備用接收者
如果一個(gè)對(duì)象實(shí)現(xiàn)了這個(gè)方法黍聂,并返回一個(gè)非nil的結(jié)果,則這個(gè)對(duì)象會(huì)作為消息的新接收者身腻,且消息會(huì)被分發(fā)到這個(gè)對(duì)象

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString *selectorString = NSStringFromSelector(aSelector);
    Class cls = NSClassFromString(@"MrHelp");
    // 將消息轉(zhuǎn)發(fā)給MrHelp類來(lái)處理
    if ([selectorString isEqualToString:@"methodTw"]) {
        return [cls new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

完整消息轉(zhuǎn)發(fā)
運(yùn)行時(shí)系統(tǒng)會(huì)在這一步給消息接收者最后一次機(jī)會(huì)將消息轉(zhuǎn)發(fā)給其它對(duì)象产还。對(duì)象會(huì)創(chuàng)建一個(gè)表示消息的NSInvocation對(duì)象,把與尚未處理的消息有關(guān)的全部細(xì)節(jié)都封裝在anInvocation中嘀趟,包括selector脐区,目標(biāo)(target)和參數(shù)。在這個(gè)方法中我們可以實(shí)現(xiàn)一些更復(fù)雜的功能她按,我們可以對(duì)消息的內(nèi)容進(jìn)行修改牛隅,比如追回一個(gè)參數(shù)等,然后再去觸發(fā)消息

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    Class cls = NSClassFromString(@"MrHelp");
    NSMethodSignature *singature= [super methodSignatureForSelector:aSelector];
    if (!singature) {
        singature = [cls instanceMethodSignatureForSelector:sel_registerName("universalMethod:AndClass:")];
    }
    return singature;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
    NSString *selectorString = NSStringFromSelector(anInvocation.selector);
    Class cls = NSClassFromString(@"MrHelp");
    id methodStr =selectorString;
    id className = [anInvocation.target class];
    [anInvocation setArgument:&methodStr atIndex:2];//第一個(gè)參數(shù)
    [anInvocation setArgument:&className atIndex:3];//第而個(gè)參數(shù)
    
    [anInvocation setSelector:sel_registerName("universalMethod:AndClass:")];//universalMethod:AndClass:AndArg:
    [anInvocation invokeWithTarget:[cls new]];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酌泰,一起剝皮案震驚了整個(gè)濱河市倔叼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宫莱,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哩罪,死亡現(xiàn)場(chǎng)離奇詭異授霸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)际插,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)碘耳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人框弛,你說(shuō)我怎么就攤上這事辛辨。” “怎么了瑟枫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵斗搞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我慷妙,道長(zhǎng)僻焚,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任膝擂,我火速辦了婚禮虑啤,結(jié)果婚禮上隙弛,老公的妹妹穿的比我還像新娘。我一直安慰自己狞山,他們只是感情好全闷,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著萍启,像睡著了一般总珠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伊约,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天姚淆,我揣著相機(jī)與錄音,去河邊找鬼屡律。 笑死腌逢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的超埋。 我是一名探鬼主播搏讶,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霍殴!你這毒婦竟也來(lái)了媒惕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤来庭,失蹤者是張志新(化名)和其女友劉穎妒蔚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體月弛,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肴盏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帽衙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菜皂。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厉萝,靈堂內(nèi)的尸體忽然破棺而出恍飘,到底是詐尸還是另有隱情,我是刑警寧澤谴垫,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布章母,位于F島的核電站,受9級(jí)特大地震影響翩剪,放射性物質(zhì)發(fā)生泄漏胳施。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一肢专、第九天 我趴在偏房一處隱蔽的房頂上張望舞肆。 院中可真熱鬧焦辅,春花似錦、人聲如沸椿胯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哩盲。三九已至前方,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廉油,已是汗流浹背惠险。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抒线,地道東北人班巩。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嘶炭,于是被迫代替她去往敵國(guó)和親抱慌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉眨猎,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,725評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,567評(píng)論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂(lè)樂(lè)的簡(jiǎn)書(shū)閱讀 2,137評(píng)論 0 9
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言抑进,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,199評(píng)論 0 7
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,939評(píng)論 1 3