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

????????關(guān)于OC的消息轉(zhuǎn)發(fā)機制,是大部分面試官在面試過程中經(jīng)常問到的問題。在此我整理了一下我對OC消息轉(zhuǎn)發(fā)機制的理解。

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

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

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

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

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

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

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

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

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

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);

第一個參數(shù)是需要添加方法的類,第二個參數(shù)是一個selector,也就是實例方法的名字,第三個參數(shù)是一個IMP類型的變量也就是函數(shù)實現(xiàn),需要傳入一個C函數(shù),這個函數(shù)至少有兩個參數(shù),一個是id self一個是SEL _cmd,第四個參數(shù)是函數(shù)類型。具體設(shè)置方法可以看注釋。

控制臺輸出:

resolveInstanceMethod: appendString:

dynamicAdditionMethodIMP

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

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

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

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

????}

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

- (id)forwardingTargetForSelector:(SEL)aSelector{

NSLog(@"forwardingTargetForSelector");


return [ForwardPerson new];

}

控制臺輸出:

forwardingTargetForSelector

?ForwardPerson===appendString:

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

#import "ForwardInvocation.h"

@implementationForwardInvocation

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

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

}

@end

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

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

? ? NSLog(@"forwardInvocation");

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

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

? ? }

}

/*必須重新這個方法,消息轉(zhuǎn)發(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? 并附消息轉(zhuǎn)發(fā)一張圖:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容