runtime01-消息機(jī)制

OC及其動(dòng)態(tài)性

Objective-C是基于C封裝的一門面向?qū)ο蟮恼Z言笛辟,底層實(shí)現(xiàn)是通過C/C++代碼實(shí)現(xiàn)的功氨。OC語言最大的特點(diǎn)就是其動(dòng)態(tài)性,它會(huì)盡可能地把決策從編譯時(shí)和連接時(shí)推遲到運(yùn)行時(shí)(簡單來說手幢,就是編譯后的文件不全是機(jī)器指令捷凄,還有一部分中間代碼,在運(yùn)行的時(shí)候围来,通過Runtime再把需要轉(zhuǎn)換的中間代碼再翻譯成機(jī)器指令)跺涤。

Runtime與消息機(jī)制

Runtime是OC的一套由C和匯編編寫的庫(一些調(diào)用頻率較高的方法是由匯編編寫的),它是OC具有動(dòng)態(tài)的的最主要條件监透。當(dāng)程序執(zhí)行[object doSomething]時(shí)桶错,會(huì)向消息接收者(object)發(fā)送一條消息(doSomething),runtime會(huì)根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)才漆。因此OC方法在運(yùn)行時(shí)牛曹,都是作為消息在傳遞的佛点,我們甚至可以把方法叫做消息醇滥,甚至可以說OC就是一門消息語言。

OC對象

在我們講消息機(jī)制前首先要了解OC的對象超营,才能了解對象的方法調(diào)用過程鸳玩。

OC的的底層其實(shí)就結(jié)構(gòu)體,長這個(gè)樣子。

OC對象.png

這個(gè)就是結(jié)構(gòu)體的內(nèi)部演闭;
由上而下不跟,objc_class這個(gè)結(jié)構(gòu)體就是一個(gè)對象,它由三部分組成米碰,

  • isa (指向?qū)ο蟾割惖闹羔?
  • superclass(它的父類)
  • cache_t(對象的調(diào)用過的方法列表)
  • bits(對象的更多信息)

class_rw_t是將bit通過位運(yùn)算的結(jié)果取其[3, 47]位窝革,轉(zhuǎn)換而成。這里包含了類的方法方法列表吕座,屬性列表及協(xié)議列表等虐译。ro就是rootclass的意思他其中包含了類的成員變量等。

OC類的繼承體系

NSString *str = [NSString string]

str是一個(gè)事例對象吴趴,它內(nèi)部的isa指針指向它的類NSString,
NSString也是一個(gè)OC類漆诽,它內(nèi)部也有isa,isa指向它的元類對象NSString meta-class (NSString的元類對象是NSString)
NSString meta-class````也是個(gè)OC類锣枝,它的isa指向它的元類meta-class厢拭。meta-class也是一個(gè)對象,它的isa指向哪里撇叁?為了防止它無限延伸下去供鸠,設(shè)計(jì)出了meta-class指向基類的meta-class以此作為它們的所屬類。即陨闹,任何NSObject繼承體系下的meta-class都使用NSObjectmeta-class```作為自己的所屬類楞捂,而基類的meta-class的isa指針是指向它自己家制。這樣就形成了一個(gè)完美的閉環(huán)。
事例如下圖泡一。

實(shí)例對象颤殴、類、元類關(guān)系

繼承體系

消息機(jī)制

已經(jīng)介紹類OC對象鼻忠,現(xiàn)在可以runtime消息機(jī)制的主題了涵但。我們的調(diào)用類方法也好,對象方法也好帖蔓,都會(huì)被轉(zhuǎn)成 objc_msgSend的消息矮瘟。由于OC的底層是由C和C++實(shí)現(xiàn)的,我們就在OC文件目錄下把它轉(zhuǎn)成C++文件塑娇。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc +要轉(zhuǎn)的OC文件
例子:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

 //定義一個(gè)Dog類澈侠,它有兩個(gè)方法一個(gè)是eat對象方法,另一個(gè)是eat類方法埋酬。
        Dog *wangCai = [[Dog alloc]init];
        [wangCai eat];
        [Dog eat];
        
//        (objc_msgSend)(wangCai, sel_registerName("eat"));  //對象方法
//        (objc_msgSend)(objc_getClass("Dog"), sel_registerName("eat"));  //類方法

這是的Dog類的.h文件

@interface Dog : NSObject
- (void)eat;
- (void)bark;
+ (void)play;
@end

我們注釋掉它的eat方法實(shí)現(xiàn)哨啃,

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
@end
消息發(fā)送

消息機(jī)制--動(dòng)態(tài)方法解析

現(xiàn)在調(diào)用eat方法它會(huì)出錯(cuò),其實(shí)OC在找不到方法實(shí)現(xiàn)的時(shí)候写妥,它會(huì)動(dòng)態(tài)調(diào)用runtime的這個(gè)方法+ (BOOL)resolveInstanceMethod:(SEL)sel

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat
//{
//    NSLog(@"dog--eat");
//}


- (void)bark{
    NSLog(@"dog--bark");
}

+ (void)play{
    NSLog(@"classfunc-dog--play");
}

/*
 2.0動(dòng)態(tài)方法解析
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(eat)) {
        // 獲取其他方法
        Method method = class_getInstanceMethod(self, @selector(bark)); //調(diào)用Dog類的bark方法拳球, 打印輸出的結(jié)果是dog--bark

        // 動(dòng)態(tài)添加test方法的實(shí)現(xiàn)
        class_addMethod(self, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));

        // 返回YES代表有動(dòng)態(tài)添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

這樣我們就實(shí)現(xiàn)了動(dòng)態(tài)給OC對象尋找實(shí)現(xiàn),防止崩潰的方法


動(dòng)態(tài)方法解析

消息機(jī)制--消息轉(zhuǎn)發(fā)

如果我們不實(shí)現(xiàn)resolveInstanceMethod程序必然會(huì)崩潰嗎珍特?別急runtime還有第二個(gè)機(jī)制防止奔潰- (id)forwardingTargetForSelector:(SEL)aSelector消息轉(zhuǎn)發(fā)機(jī)制祝峻,你不是處理不了嗎?那你吧消息轉(zhuǎn)給別人扎筒,讓有能力的類處理莱找。
我們定義一個(gè)處理這eat方法的Cat類,.h的聲明寫不寫都成嗜桌,因?yàn)樗鼤?huì)直接在方法實(shí)現(xiàn)中搜取

#import "Cat.h"

@implementation Cat

- (void)eat{
    NSLog(@"Cat--eat");
}
@end

我們在Dog的類中需要做如下處理奥溺,把消息轉(zhuǎn)發(fā)給Cat讓Cat幫它去處理

///Dog.m類
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [[Cat alloc] init]; //返回空否
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

//這樣處理Cat的eat類會(huì)被調(diào)用,打印出Cat--eat

當(dāng)然消息轉(zhuǎn)發(fā)的時(shí)候也不知道轉(zhuǎn)給誰(即- (id)forwardingTargetForSelector:(SEL)aSelector返回的是空對象nil),可以在- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法里自己生成個(gè)簽名症脂,然后實(shí)現(xiàn)
- (void)forwardInvocation:(NSInvocation *)anInvocation方法谚赎,收集日志防止程序崩潰

///Dog.m類
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息轉(zhuǎn)發(fā)
 */
// 方法簽名:返回值類型、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [NSMethodSignature signatureWithObjCTypes:"@@:*"];//手動(dòng)創(chuàng)建一個(gè)方法簽名
    }

    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息轉(zhuǎn)發(fā)
 */
//自定義的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"調(diào)用的方法找不到實(shí)現(xiàn)");
}

如果你這會(huì)兒知道誰能處理這個(gè)消息诱篷,也可以這樣處理壶唤,

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@interface Dog ()
@property (nonatomic,strong) Cat *miCat;
@end

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
/*
 3.0 消息轉(zhuǎn)發(fā)
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息轉(zhuǎn)發(fā)
 */
// 方法簽名:返回值類型、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(eat)) {
        self.miCat = [Cat new];
        return [self.miCat methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息轉(zhuǎn)發(fā)
 */
//自定義的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    NSLog(@"調(diào)用的方法找不到實(shí)現(xiàn)");
    if (anInvocation.selector == @selector(eat)) {
        [anInvocation invokeWithTarget:self.miCat];
    }
}
@end

/// Cat類的eat方法同樣會(huì)被調(diào)用棕所,打印出Cat--eat
消息轉(zhuǎn)發(fā)

消息鏈總計(jì)起來如下:

  • 1.查找

1.本類查找方法闸盔,若有響應(yīng),若無去父類查找琳省;

  1. 父類查找迎吵,若有響應(yīng)躲撰,如無去父類查找,直至元類;
  2. 元類有響應(yīng)击费,元類無拢蛋,走消息分發(fā)機(jī)制
  • 2.消息轉(zhuǎn)發(fā)

1.消息重新交給被掉用類,被掉用類可以讓自己別的方法替代響應(yīng)

  • 3.動(dòng)態(tài)方法解析
  1. 被掉用類將方法拋給指定的類蔫巩, 讓它響應(yīng)該方法
  • 4.消息轉(zhuǎn)發(fā)

1.方法重新回到被掉用類自身谆棱,被掉用類,手動(dòng)生成方法簽名

  1. 將改消息交給指定的類圆仔,讓它體自己響應(yīng)

方法查找不到時(shí)垃瞧,只有(2、3坪郭、4)三層保護(hù)全沒有處理才會(huì)報(bào)錯(cuò)个从。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歪沃,隨后出現(xiàn)的幾起案子嗦锐,更是在濱河造成了極大的恐慌,老刑警劉巖绸罗,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件意推,死亡現(xiàn)場離奇詭異,居然都是意外死亡珊蟀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門外驱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來育灸,“玉大人,你說我怎么就攤上這事昵宇“跽福” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵瓦哎,是天一觀的道長砸喻。 經(jīng)常有香客問我,道長蒋譬,這世上最難降的妖魔是什么割岛? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮犯助,結(jié)果婚禮上癣漆,老公的妹妹穿的比我還像新娘。我一直安慰自己剂买,他們只是感情好惠爽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布癌蓖。 她就那樣靜靜地躺著,像睡著了一般婚肆。 火紅的嫁衣襯著肌膚如雪租副。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天较性,我揣著相機(jī)與錄音附井,去河邊找鬼。 笑死两残,一個(gè)胖子當(dāng)著我的面吹牛永毅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播人弓,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼沼死,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了崔赌?” 一聲冷哼從身側(cè)響起意蛀,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎健芭,沒想到半個(gè)月后县钥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慈迈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年若贮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痒留。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谴麦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伸头,到底是詐尸還是另有隱情山上,我是刑警寧澤还绘,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響鬼廓,放射性物質(zhì)發(fā)生泄漏援所。R本人自食惡果不足惜蝙云,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一酸员、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锌妻,春花似錦代乃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽原茅。三九已至,卻和暖如春堕仔,著一層夾襖步出監(jiān)牢的瞬間擂橘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工摩骨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留通贞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓恼五,卻偏偏與公主長得像昌罩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子灾馒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉茎用,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評論 0 9
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,129評論 0 9
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢睬罗?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,172評論 0 7
  • 一轨功、Runtime簡介 Runtime簡稱運(yùn)行時(shí)。OC就是運(yùn)行時(shí)機(jī)制容达,也就是在運(yùn)行時(shí)候的一些機(jī)制古涧,其中最主要的是消...
    林安530閱讀 1,059評論 0 2
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 727評論 0 2