個人對iOS OC 消息轉發(fā)機制的基本原理理解

????????關于OC的消息轉發(fā)機制摇邦,是大部分面試官在面試過程中經常問到的問題转培。在此我整理了一下我對OC消息轉發(fā)機制的理解炸宵。

? ????? 眾所周知OC的一個對象在發(fā)送消息的時候首先在cache里找钟鸵,如果找不到就在該類的struct objc_method_list列表中去搜索性昭,如果找到則直接調用相關方法的實現渣蜗,如果沒有找到就會通過super_class指針沿著繼承樹向上去搜索屠尊,如果找到就跳轉,如果到了繼承樹的根部(通常為NSObject)還沒有找到袍睡。那就會調用NSObjec的一個方法doesNotRecognizeSelector:知染,這樣就會報unrecognized selector 錯誤。其實在調用doesNotRecognizeSelector:方法之前還會進行消息轉發(fā)---還有三次機會來補救斑胜。也就是常說的OC消息轉發(fā)的三次補救措施控淡。

????????總的來說一個OC消息的發(fā)送會經歷四個階段(該四個階段都是搜索到NSObject再進入下階段)

? ? ? ? 1)先在本類中搜索改方法的實現,如果有則直接調用若果沒有則去父類中搜索直到NSObject止潘,如果NSObject沒有則進入消息轉發(fā)(類的動態(tài)方法解析掺炭、備用接受者對象、完整的消息轉發(fā))凭戴。

????????2)類的動態(tài)方法解析:

? ? ? ? 首先創(chuàng)建SonPerson類涧狮,在ViewController 里面寫

? ????????id person = [[SonPerson alloc]init];

? ? ????[person appendString:@""];

? ? ????注意這里要用id 不然編譯報錯。

????????在該類和父類中沒有找到該方法的實現則會執(zhí)行 +(BOOL)resolveClassMethod:(SEL)sel 或+(BOOL)resolveInstanceMethod:(SEL)sel 方法么夫。在+(BOOL)resolveClassMethod:(SEL)sel 或+(BOOL)resolveInstanceMethod:(SEL)sel 方法 中利用黑魔法runtime 動態(tài)添加方法實現者冤。

void dynamicAdditionMethodIMP(id self,SEL _cmd){

? ? NSLog(@"dynamicAdditionMethodIMP");

}

+(BOOL)resolveClassMethod:(SEL)sel{

? ? NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));

? ? if(sel ==@selector(appendString:)) {

? ? ? ? class_addMethod([selfclass], sel, (IMP)dynamicAdditionMethodIMP,"v@:");

? ? ? ? returnYES;

? ? }

? ? return[superresolveClassMethod:sel];

}

+(BOOL)resolveInstanceMethod:(SEL)sel{

? ? NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));

? ? if(sel ==@selector(appendString:)) {

? ? ? ? class_addMethod([selfclass], sel, (IMP)dynamicAdditionMethodIMP,"v@:");

? ? ? ? returnYES;

? ? }

? ? return[super resolveInstanceMethod:sel];

}

BOOL class_addMethod(Class cls, SEL name, IMP imp,constchar*types);

第一個參數是需要添加方法的類,第二個參數是一個selector档痪,也就是實例方法的名字涉枫,第三個參數是一個IMP類型的變量也就是函數實現,需要傳入一個C函數腐螟,這個函數至少有兩個參數愿汰,一個是id self一個是SEL _cmd困后,第四個參數是函數類型。具體設置方法可以看注釋衬廷。

控制臺輸出:

resolveInstanceMethod: appendString:

dynamicAdditionMethodIMP

? ? 2)備用接受者: 在+(BOOL)resolveClassMethod:(SEL)sel 或+(BOOL)resolveInstanceMethod:(SEL)sel 方法返回NO的時候進入備用接受者階段 摇予。

? ? 創(chuàng)建一個備用接受者類ForwardPerson 實現appendString:方法

????-(void)appendString:(NSString*)str{

? ? ????NSLog(@"%@===%@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));

????}

? ? 在SonPerson類中實現- (id)forwardingTargetForSelector:(SEL)aSelector 方法 并返回一個備用接受者對象?

- (id)forwardingTargetForSelector:(SEL)aSelector{

NSLog(@"forwardingTargetForSelector");


return [ForwardPerson new];

}

控制臺輸出:

forwardingTargetForSelector

?ForwardPerson===appendString:

? ??3)完整的消息轉發(fā):當-(void)forwardInvocation:(NSInvocation*)anInvocation 方法方法nil的時候則進入消息轉發(fā)的最后階段,完整的消息轉發(fā)吗跋。也需要創(chuàng)建一個轉發(fā)對象ForwardInvocation?

#import "ForwardInvocation.h"

@implementationForwardInvocation

-(void)appendString:(NSString*)str{

? ? NSLog(@"%@===%@",NSStringFromClass([self class]),NSStringFromSelector(_cmd));

}

@end

? ? 在SonPerson中實現-(void)forwardInvocation:(NSInvocation*)anInvocation和- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector方法

-(void)forwardInvocation:(NSInvocation*)anInvocation{

? ? NSLog(@"forwardInvocation");

? ? if ([ForwardInvocation instancesRespondToSelector:anInvocation.selector]) {

? ? ? ? [anInvocation invokeWithTarget:self.invocation];

? ? }

}

/*必須重新這個方法侧戴,消息轉發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象 返回nil上面方法不執(zhí)行*/

- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{

? ? NSMethodSignature*signature = [super methodSignatureForSelector:aSelector];

? ? if(!signature){

? ? ? ? if ([ForwardInvocation instancesRespondToSelector:aSelector]){

? ? ? ? ? ? signature = [ForwardInvocation instanceMethodSignatureForSelector:aSelector];

? ? ? ? }

? ? }

? ? returnsignature;

}

控制臺輸出:

forwardInvocation

ForwardInvocation===appendString:

最后附Demo:GitHub - SionChen/OBJC_SendMsg? 并附消息轉發(fā)一張圖:


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市小腊,隨后出現的幾起案子救鲤,更是在濱河造成了極大的恐慌,老刑警劉巖秩冈,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件本缠,死亡現場離奇詭異,居然都是意外死亡入问,警方通過查閱死者的電腦和手機丹锹,發(fā)現死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芬失,“玉大人楣黍,你說我怎么就攤上這事±饫茫” “怎么了租漂?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長颊糜。 經常有香客問我哩治,道長,這世上最難降的妖魔是什么衬鱼? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任业筏,我火速辦了婚禮,結果婚禮上鸟赫,老公的妹妹穿的比我還像新娘蒜胖。我一直安慰自己,他們只是感情好抛蚤,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布台谢。 她就那樣靜靜地躺著,像睡著了一般岁经。 火紅的嫁衣襯著肌膚如雪对碌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天蒿偎,我揣著相機與錄音朽们,去河邊找鬼。 笑死诉位,一個胖子當著我的面吹牛骑脱,可吹牛的內容都是我干的。 我是一名探鬼主播苍糠,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼叁丧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了岳瞭?” 一聲冷哼從身側響起拥娄,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞳筏,沒想到半個月后稚瘾,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡姚炕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年摊欠,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柱宦。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡些椒,死狀恐怖,靈堂內的尸體忽然破棺而出掸刊,到底是詐尸還是另有隱情免糕,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布忧侧,位于F島的核電站石窑,受9級特大地震影響,放射性物質發(fā)生泄漏苍柏。R本人自食惡果不足惜尼斧,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望试吁。 院中可真熱鬧棺棵,春花似錦、人聲如沸熄捍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽余耽。三九已至缚柏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碟贾,已是汗流浹背币喧。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工轨域, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杀餐。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓干发,卻偏偏與公主長得像,于是被迫代替她去往敵國和親史翘。 傳聞我的和親對象是個殘疾皇子枉长,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353