iOS中的消息轉(zhuǎn)發(fā)機制

OC由于運行時特性,可以在運行期間動態(tài)添加方法,這個尋找動態(tài)添加的方法的過程就是動態(tài)消息轉(zhuǎn)發(fā)。
iOS的消息轉(zhuǎn)發(fā)機制分為三個步驟:

  • 動態(tài)方法解析
  • 快速消息轉(zhuǎn)發(fā)機制
  • 完整消息轉(zhuǎn)發(fā)機制
消息轉(zhuǎn)發(fā)機制
1. 動態(tài)方法解析

首先是征詢接收者所屬的類,看其是否能動態(tài)添加調(diào)用的方法,來處理當(dāng)前這個未知的選擇子;

對象在無法解讀消息會首先調(diào)用所屬類的下列類方法:
+ (BOOL) resolveInstanceMethod:(SEL)selector

參數(shù)為那個未知的選擇子,返回值表示這個類能否新增一個實例方法處理此選擇子。如果尚未實現(xiàn)的方法不是實例方法而是類方法則運行期會調(diào)用另一個方法:+ (BOOL) resolveClassMethod:(SEL)selector。使用這種方法的前提是:相關(guān)方法的實現(xiàn)代碼已經(jīng)寫好,只等著運行的時候動態(tài)插入到類里面就可以了。此方案常用來實現(xiàn)@dynamic屬性。

2. 快速消息轉(zhuǎn)發(fā)

當(dāng)前接收者還有第二次機會能處理未知的選擇子,這一步中,運行期會問它:能不能把這條消息轉(zhuǎn)發(fā)給其他接收者來處理。與該步驟對應(yīng)的處理方法:
- (id)forwardingTargetForSelector:(SEL)selector

方法參數(shù)代表未知的選擇子,若當(dāng)前接收者能找到備援對象,則將其返回,若找不到就返回nil。通過此方案我們可以用“組合”來模擬出“多重繼承”的某些特性(因為OC屬于單繼承,一個字類只能繼承一個基類)。在一個對象內(nèi)部,可能還有一系列其他對象,該對象可能由此方法將能夠處理某選擇子的相關(guān)內(nèi)部對象返回,這樣的話,在外界看來,好像該對象親自處理了這些消息。

- (id)forwardingTargetForSelector:(SEL)aSelector  
{  
    TargetObj *obj = [[TargetObj alloc]init];  
    if ([obj respondsToSelector:aSelector]) {  
        return doctor;  
    }  
    return nil;  
} 
3. 完整消息轉(zhuǎn)發(fā)

這一步是消息轉(zhuǎn)發(fā)的最后一步,首先會通過以下兩個方法

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

獲得函數(shù)的參數(shù)和返回值,如果返回nil,runtime則會發(fā)出doesNotRecognizeSelector消息,然后crash;
若是返回了一個函數(shù)簽名,runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送- (void)forwardInvocation:(NSInvocation *)Invocation 消息給目標(biāo)對象

#pragma mark - 3、完整消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"forwardInvocation");

    if ([RuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:_helper];
    }
}

/*必須重新這個方法,消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature){
        if ([RuntimeMethodHelper instancesRespondToSelector:aSelector]){
            signature = [RuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

示例代碼:

#import "Developer.h"
#import "Finance.h"
#import <objc/runtime.h>

@implementation Developer

- (void)doDeveloper {
    NSLog(@"Developer doWork!");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    /*
     如果當(dāng)前對象調(diào)用了一個不存在的方法
     Runtime會調(diào)用resolveInstanceMethod:來進行動態(tài)方法解析
     我們需要用class_addMethod函數(shù)完成向特定類添加特定方法實現(xiàn)的操作
     返回NO,則進入下一步forwardingTargetForSelector:
     */
    /*
    class_addMethod(self,
                    sel,
                    class_getMethodImplementation(self, sel_registerName("doDeveloper")),
                    "v@:");
    return [super resolveInstanceMethod:sel];
     */
    return NO;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    /*
     在消息轉(zhuǎn)發(fā)機制執(zhí)行前,Runtime 系統(tǒng)會再給我們一次重定向的機會
     通過重載forwardingTargetForSelector:方法來替換消息的接受者為其他對象
     返回nil則進步下一步forwardInvocation:
     */
    /*
    return [[Finance alloc] init];
     */
    return nil;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    
    /*
     獲取方法簽名進入下一步,進行消息轉(zhuǎn)發(fā)
     */
    
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    /*
     消息轉(zhuǎn)發(fā)
     */
    
    return [anInvocation invokeWithTarget:[[Finance alloc] init]];
}
最后編輯于
?著作權(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)容