OC中的消息轉(zhuǎn)發(fā)

OC是消息型語言,OC中的方法調(diào)用實際上是消息的發(fā)送,編譯器并不能決定程序真正執(zhí)行的到底是哪段代碼,這個工作,需要運行時系統(tǒng)來完成

消息發(fā)送

舉個例子:alloc init初始化方法 我們玩玩用消息發(fā)送的方式
使用之前記得配置一下,官方是不推薦我們直接使用這種的,應該說是不讓我們直接這樣用。這里只是玩玩,實際開發(fā)中不要這樣弄了
配置好后,導入頭文件 <objc/message.h>


配置.png
//使用消息發(fā)送的形式 進行對象的創(chuàng)建與初始化 方法調(diào)用
penson *p = objc_msgSend([penson class], @selector(alloc));
p = objc_msgSend(p, @selector(init));
objc_msgSend(p, @selector(run));
//試試自己創(chuàng)建一個類 然后run  NSLog(@"跑");

正題:消息轉(zhuǎn)發(fā)

[penson run]: unrecognized selector sent to instance 0x604000001c70

熟不熟悉,對象不能響應方法調(diào)用,大致就是這樣的。說白了,就是調(diào)用的方法未找到實現(xiàn)
當對象收到消息后,會在自己的方法列表中查找是否有該方法。如果沒有,那么找父類,一直找到NSObject。 如果都沒有那么就開始那個消息轉(zhuǎn)發(fā)三部曲了。
簡單點:當一個對象收到消息,會先在自己的繼承體系中尋找實現(xiàn),如果處理不了。那么就進行三步走

第一步,是否動態(tài)的添加方法

這一步是給我們的第一次挽救機會,我們可以在這里動態(tài)的添加一個方法。如果我們在這里處理了,那么下面的兩步不會走到

//.m 中
//首先認識兩個方法
//+ (BOOL)resolveInstanceMethod:(SEL)sel;//調(diào)用的是對象方法
//+ (BOOL)resolveClassMethod:(SEL)sel;//響應的是類方法

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    //加個判斷
    if (sel == @selector(run)) {
      class_addMethod(self, sel, (IMP)imp, "v@:");
    }
     return [super resolveInstanceMethod:sel];
    //v@:   
    //第一個參數(shù)代表返回值: v代表無返回值()  
    //第二個參數(shù)與第三個參數(shù)是固定的模式:@代表self  :代表_cmd;
    //如果有參數(shù)可以加上第四個
}
+ (BOOL)resolveClassMethod:(SEL)sel {
    class_addMethod(objc_getMetaClass(object_getClassName(self)), sel, (IMP)imp, "");
    return [super resolveClassMethod:sel];
}
void imp(id self, SEL _cmd) {
//id self SEL _cmd  兩個隱藏的參數(shù) 一定會有的
    NSLog(@"沒有找到相應方法");
}
第二步,是否將消息轉(zhuǎn)發(fā)給其它對象(備援接受者)

第二次挽救機會,如果在動態(tài)添加方法那里并沒有做什么處理。那么就會走到這一步。給你第二次挽救的機會

//.m 中
- (id)forwardingTargetForSelector:(SEL)aSelector {
    //這里就是讓你把消息轉(zhuǎn)發(fā)給別人  讓別人響應去
    //如果別人也沒有實現(xiàn)  那么還是會報錯
    //這里的返回值如果是nil 或者 自身    表示轉(zhuǎn)發(fā)給自己或者不轉(zhuǎn)發(fā)  沒什么意義了  最后肯定會報錯
    return nil;
}
第三步,完整的消息轉(zhuǎn)發(fā)

如果在前兩步都不作為,那么就到這最后一步了。最后一次挽救的機會。
這一步中,會創(chuàng)建NSInvocation對象,把與尚未處理的那條消息有關的全部細節(jié)都封于其中。此對象包含選擇子、目標及參數(shù)。在觸發(fā)NSInvocation對象時,“消息派發(fā)系統(tǒng)”把消息指派給目標對象。讓目標對象進行處理

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    //返回一個方法簽名
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    //消息派發(fā)給其它目標對象 而且可以派發(fā)給多個對象
    //派發(fā)之前可以進行判斷一下
    SEL selector =[anInvocation selector];
    Ppenson *p1=[Ppenson new];
    PPpenson *p2=[PPpenson new];
    if ([p1 respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:p1];
    }
    if ([p2 respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:p2];
    }
}

題外話 alloc init

alloc:給對象開辟一片內(nèi)存空間
init:對這片空間進行初始化
.有說alloc出來的并不是一個真正的NSObject對象 所以不能直接使用
.那么不知道有沒有試過直接alloc 并不進行init 你會發(fā)現(xiàn)依然能夠調(diào)用到對象中的方法
.既然調(diào)用了方法 難道不算是使用了這個對象 這一點我很疑惑
.關于這一點疑惑,看看init的作用 或許明白一丟丟
.init是對這片空間的初始化 如果不對這片空間進行初始化 那么這片空間是否還會存有數(shù)據(jù)。
.關于是否存有數(shù)據(jù)這一點 不能確定。只能說當某個對象釋放的時候,他之前占用的內(nèi)存空間并不會把數(shù)據(jù)給清理掉,只是告訴系統(tǒng)這塊空間可以分配出去了。
.那么也就是說 如果不進行init操作 我們雖然能夠進行訪問 但是是不安全的 可能訪問到了其它莫名的數(shù)據(jù)
.所以為了安全考慮 推薦使用init初始化一下

v@: 說明
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100

知識鏈接:
https://www.csdn.net/article/2015-07-06/2825133-objective-c-runtime/1 <runtime知識 很是詳盡>
http://www.cocoachina.com/ios/20150818/13075.html <方法緩存>

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

相關閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結構(3).初始化時...
    歐辰_OSR閱讀 30,215評論 8 265
  • OC語言基礎 1.類與對象 類方法 OC的類方法只有2種:靜態(tài)方法和實例方法兩種 在OC中,只要方法聲明在@int...
    奇異果好補閱讀 4,517評論 0 11
  • 莫名的煩 什么什么都不想去做 沒心情 躁得很。一般下雪的時候不這樣?。?晚安!
    約旦手抓餅閱讀 286評論 0 0
  • 過了年,你已經(jīng)可以叫11歲了(實際年齡為9歲4個月),可是對于你的獨立自理能力來說,我覺得自己教育的很失敗,...
    會飛的塵埃閱讀 259評論 0 0
  • 今天傍晚出門吃飯,我穿了一條橙紅色的短裙剛好配自己非常喜歡的一件黑色羊絨長大衣。 因為好久沒有穿過膝長靴了,靴子需...
    楚浛閱讀 1,169評論 8 19

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