iOS底層原理之消息發(fā)送

OC中的方法調(diào)用,其實都是轉(zhuǎn)化為objc_msgSend函數(shù)的調(diào)用,objc_mesgSend的執(zhí)行流程可分為3個階段

  • 消息發(fā)送
  • 動態(tài)方法解析
  • 消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)的流程圖

動態(tài)方法解析

動態(tài)方法解析

開發(fā)者可以實現(xiàn)以下的辦法來實現(xiàn)動態(tài)添加方法實現(xiàn)

  • +resolveInstanceMethod:
  • +resolveClassMethod:

動態(tài)解析后會重新走消息發(fā)送的流程,從receiveClasscache中查找方法這一步開始執(zhí)行

創(chuàng)建一個Person類,然后在.h文件中寫一個- (void)test,但是不寫具體實現(xiàn),然后調(diào)用.會打印出最常見的unrecognized selector sent to instance.

- (void)other{
NSLog(@"%s",__func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(test)) {
//獲取其他方法
Method method = class_getInstanceMethod(self, @selector(other));
//動態(tài)添加test的方法
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}

消息轉(zhuǎn)發(fā)

如果一個方法在消息發(fā)送階段沒有找到相關(guān)方法,也沒有進行動態(tài)方法解析,這個時候就會走到消息轉(zhuǎn)發(fā)階段了.

消息轉(zhuǎn)發(fā)

forwardingTargetForSelector

創(chuàng)建兩個類Person和Student,在Person.h里面寫一個實例方法,但是不去實現(xiàn)相關(guān)方法。

在Person里面實現(xiàn)這個方法
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[Student alloc]init];
    }
    return nil;
}

調(diào)用forwardingTargetForSelector,返回值不為nil時,會調(diào)用objc_msgSend(返回值, SEL),結(jié)果就是調(diào)用了objc_msgSend(Student,test)

methodSignatureForSelector(方法簽名)

forwardingTargetForSelector返回值為nil,或者都沒有調(diào)用該方法的時候,系統(tǒng)會調(diào)用methodSignatureForSelector方法。調(diào)用methodSignatureForSelector,返回值不為nil,調(diào)用forwardInvocation:方法;返回值為nil時,調(diào)用doesNotRecognizeSelector:方法

對于方法簽名的生成方式

  • [NSMethodSignature signatureWithObjCTypes:"i@:i"]
  • [[[Student alloc]init] methodSignatureForSelector:aSelector];
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[[Student alloc]init]methodSignatureForSelector:aSelector];
       // return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"=======");
}

NSInvocation中封裝了一個方法調(diào)用,包括方法調(diào)用者,方法名,方法參數(shù).

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

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