Runtime的執(zhí)行過程(二)

接著上一篇Runtime的執(zhí)行過程(一)许起,之前說了runtime的交互莉撇、runtime的消息機制皿曲、傳遞棵红、方法地址獲取噪舀。

四垢油、Dynamic Method Resolution

? 動態(tài)方法解析静汤,就是runtime提供了給類動態(tài)添加方法的實現(xiàn)琅催。比如類的屬性,會自動添加setter和getter方法就是如此虫给,在編譯時藤抡,將動態(tài)的添加與該屬性相關聯(lián)的setter和getter方法。
?可以通過實現(xiàn)這些方法resolveInstanceMethod:抹估,resolveClassMethod:缠黍,并分別為實例和類方法動態(tài)提供給定選擇器的實現(xiàn)。

/// C函數(shù)药蜻,本身就是一個IMP指針
void dynamicMethodIMP(id self瓷式,SEL _cmd){
    //實施....
}

@implementation MyClass
+(BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if(aSEL == @selector(resolveThisMethodDynamically)) {
          class_addMethod([self class], aSEL, (IMP)dynamicMethodIMP, “v@:”);
          return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}
@end

我們看到,動態(tài)添加方法使用C函數(shù):

class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types);

第一個參數(shù)是類语泽,即當前對象的類贸典。
第二個是一個方法選擇器,我理解就是一個方法名踱卵。
第三個是一個指向實現(xiàn)的函數(shù)指針廊驼,如果是C函數(shù)直接強轉即可,如果是OC方法惋砂,則需要獲取蔬充。可以使用以下方法獲劝嗬:

IMP imp = class_getMethodImplementation([receiver class], @selector(message));
或者
Method m = class_getInstanceMethod([receiver class], @selector(message));
IMP imp = method_getImplementation(m);

第四個是方法的編碼饥漫,一組描述方法、返回值及參數(shù)類型特征的字符罗标。
? 比如加入的方法是int sum(id self, SEL _cmd, int a, int b)庸队,那么這里是:class_addMethod([receiver class], @selector(sum), (IMP) sum, "i@:ii");i表示int(An int闯割,返回值類型)彻消,@表示self(An object,Object類型)宙拉,:表示_cmd(A method selector (SEL)宾尚,方法選擇器類型),ii分別表示后面兩個參數(shù)(An int,int類型)煌贴。這里蘋果有提供一個類型對照表:

image.png
image.png

通常消息轉發(fā)和動態(tài)方法解析是互不相干的御板。在進入消息轉發(fā)機制之前, respondsToSelector:和instancesRespondToSelector: 會被首先調用牛郑。您可以在這兩個 方法中為傳進來的選標提供一個IMP怠肋。如果您實現(xiàn)了resolveInstanceMethod:方法但是仍然希望正 常的消息轉發(fā)機制進行,您只需要返回NO就可以了淹朋。

Dynamic Loading

動態(tài)加載可以用在很多地方笙各。例如,系統(tǒng)配置中的模塊就是被動態(tài)加載的础芍。
在 Cocoa 環(huán)境中杈抢,動態(tài)加載一般被用來對應用程序進行定制。您的程序可以在運行時加載其他程序員編寫的模塊——和 Interface Build 載入定制的調色板以及系統(tǒng)配置程序載入定制的模塊的類似仑性。 這些模塊通過 您許可的方式擴展了您的程序春感,而您無需自己來定義或者實現(xiàn)。您提供了框架虏缸,而其它的程序員提供了實 現(xiàn)。
盡管已經有一個運行時系統(tǒng)的函數(shù)來動態(tài)加載Mach-O文件中的Objective-C模塊 (objc_loadModules嫩实,在objc/objc-load.h中定義)刽辙,Cocoa的NSBundle類為動態(tài)加載 提供了一個更方便的接口——一個面向對象的,已和相關服務集成的接口甲献。關于NSBundle類的更多相關
信息請參考Foundation框架中關于NSBundle類的文檔宰缤。關于Mach-O文件的有關信息請參考Mac OS X ABI Mach-O 文件格式參考庫。

五晃洒、Message Forwarding

通常慨灭,給一個對象發(fā)送它不能處理的消息會得到出錯提示,然而球及,Objective-C 運行時系統(tǒng)在拋出錯誤之前氧骤, 會給消息接收對象發(fā)送一條特別的消息來通知該對象。

如果一個對象收到一條無法處理的消息吃引,運行時系統(tǒng)會在拋出錯誤前筹陵,給該對象發(fā)送一條
forwardInvocation:消息,該消息的唯一參數(shù)是個 NSInvocation 類型的對象——該對象封裝了 原始的消息和消息的參數(shù)镊尺。
您可以實現(xiàn) forwardInvocation:方法來對不能處理的消息做一些默認的處理朦佩,也可以以其它的某種 方式來避免錯誤被拋出。如 forwardInvocation:的名字所示庐氮,它通常用來將消息轉發(fā)給其它的對象语稠。

為了方便我們理解,文檔給了這么一個場景:首先弄砍,假設您正在設計一個可以響應方法名為 negotiate 的消息的對象仙畦,并且你希望它的響應跟另外一個對象一樣输涕。這時候,你可以通過將 negotiate 消息傳遞給你已經實現(xiàn)了 negotiate 方法主體中某處的其他對象來輕松完成此操作议泵。
?更進一步占贫,假設你希望對象對 negotiate 消息的響應與在另一個類中實現(xiàn)的響應完全相同。實現(xiàn)此目的的一種方法是讓你的類從另一個類繼承該方法先口。然而型奥,這樣安排事情可能是不可能的。你的類和實現(xiàn)協(xié)商的類在繼承層次結構的不同分支中可能有很好的理由碉京。
?這個時候厢汹,即使你的類不能繼承negotiate方法,你仍然可以通過實現(xiàn)一個簡單地將消息傳遞給另一個類的實例的方法版本來“借用”它:

  - (id)negotiate
  {
      if ( [someOtherObject respondsTo:@selector(negotiate)] )
          return [someOtherObject negotiate];
      return self;
  }

這是時候谐宙,調用** negotiate**就會間接調用了someOtherObject的negotiate方法烫葬。
但是,如果當你需要將大量消息傳遞給另一個對象時凡蜻,這種方式就會變得很麻煩搭综,因為你必須提前寫好方法所有你想轉發(fā)的方法集,但是你現(xiàn)在可能并不知道完整的方法集划栓。
?forwardInvocation: 消息為這個問題提供了一個不太特別的第二次機會解決方案兑巾,而且是動態(tài)的。它的觸發(fā)時機是忠荞,當一個對象因為沒有與消息中的選擇器匹配的方法而無法響應消息時蒋歌,運行時系統(tǒng)通過向它發(fā)送一個 forwardInvocation: 消息來通知該對象。每個對象都從 NSObject 類繼承一個 forwardInvocation: 方法委煤。然而堂油,NSObject 版本的方法只是調用了 dosNotRecognizeSelector:。通過覆蓋 NSObject 的版本并實現(xiàn)您自己的版本碧绞,你可以利用 forwardInvocation: 消息提供的機會將消息轉發(fā)到其他對象府框。

要轉發(fā)消息,所有 forwardInvocation: 方法需要做的是:
1讥邻、 確定消息應該去哪里
2寓免、 將其連同其原始參數(shù)發(fā)送到那里〖莆可以使用 invokeWithTarget: 方法發(fā)送消息:

  - (void)forwardInvocation:(NSInvocation *)anInvocation
  {
      if ([someOtherObject respondsToSelector:
              [anInvocation selector]])
          [anInvocation invokeWithTarget:someOtherObject];
      else
          [super forwardInvocation:anInvocation];
  }

Forwarding and Multiple Inheritance (消息轉發(fā)和多繼承)

消息轉發(fā)模仿繼承袜香,可用于將多重繼承的某些效果借給 Objective-C 程序。如圖所示鲫惶,通過轉發(fā)消息來響應消息的對象似乎借用或“繼承”了另一個類中定義的方法實現(xiàn)蜈首。(相當于OC借用消息轉發(fā)實現(xiàn)了類似C++中的多繼承)

forwarding.gif

在此這個圖中,Warrior 類的一個實例將 negotiate 消息轉發(fā)給 Diplomat 類的一個實例。Warrior看起來像外交官一樣談判欢策。它似乎會回應談判信息吆寨,并且出于所有實際目的,它確實會做出回應(盡管它實際上是一名外交官在做這項工作)踩寇。
實際上啄清,消息轉發(fā)也的確獲得了多繼承的大部分功能,但是他們本身還是有區(qū)別的:

  1. 多重繼承在單個對象中結合了不同的功能俺孙。它傾向于大型的辣卒、多方面的對象。
  2. 轉發(fā)將不同的職責分配給不同的對象睛榄。它將問題分解為更小的對象荣茫,但以對消息發(fā)送者透明的方式關聯(lián)這些對象

Forwarding and Inheritance (消息轉發(fā)和繼承)

盡管轉發(fā)模仿繼承,但 NSObject 類從不混淆兩者场靴。像 respondsToSelector: 和 isKindOfClass: 這樣的方法只看繼承層次啡莉,從不看轉發(fā)鏈。例如旨剥,如果詢問 Warrior 對象是否響應協(xié)商消息咧欣,

  if ( [aWarrior RespondsToSelector:@selector(negotiate)] ) {
      return NO;
  }

答案是無法響應,即使它可以毫無錯誤地接收 negotiate 消息并在某種意義上通過將它們轉發(fā)給外交官來響應它們轨帜。如果你想讓你的對象表現(xiàn)得好像它們真的繼承了它們轉發(fā)消息的對象的行為魄咕,你需要重新實現(xiàn) respondsToSelector: 和 isKindOfClass: 方法來包含你的轉發(fā)算法。

  - (BOOL)respondsToSelector:(SEL)aSelector
  {
      if ( [super respondsToSelector:aSelector] )
          return YES;
      else {
          /* Here, test whether the aSelector message can     *
           * be forwarded to another object and whether that  *
           * object can respond to it. Return YES if it can.  */
          if ([aSelector isEqualTo:@selector(negotiate)]) {
              return [[someOtherObject respondsToSelector:aSelector]];
          }
      }
      return NO;
  }

除了 respondsToSelector: 和 isKindOfClass: 之外阵谚,instanceRespondToSelector: 方法也需要反映轉發(fā)算法。如果使用協(xié)議烟具,則應將conformsToProtocol: 方法同樣添加到列表中梢什。
??類似地,如果一個對象轉發(fā)它接收到的任何遠程消息朝聋,它應該有一個 methodSignatureForSelector 版本:它可以返回最終響應轉發(fā)消息的方法的準確描述嗡午;例如,如果一個對象能夠將消息轉發(fā)給它的代理冀痕,您將實現(xiàn) methodSignatureForSelector:

  - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
  {
      NSMethodSignature* signature = [super methodSignatureForSelector:selector];
      if (!signature) {
         signature = [surrogate methodSignatureForSelector:selector];
      }
      return signature;
  }

蘋果提醒我們荔睹,消息轉發(fā)不是為了作為多繼承的替代品,僅僅適用于沒有其他解決方案的情況言蛇。如果確實要使用的話僻他,需要確保你真的理解進行轉發(fā)的類和轉發(fā)到類的行為。

  Note:  This is an advanced technique, suitable only for situations where no other solution is possible. It is   
  not intended as a replacement for inheritance. If you must make use of this technique, make sure you
  fully understand the behavior of the class doing the forwarding and the class you’re forwarding to.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末腊尚,一起剝皮案震驚了整個濱河市吨拗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖劝篷,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哨鸭,死亡現(xiàn)場離奇詭異,居然都是意外死亡娇妓,警方通過查閱死者的電腦和手機像鸡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哈恰,“玉大人只估,你說我怎么就攤上這事∪锘龋” “怎么了仅乓?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蓬戚。 經常有香客問我夸楣,道長,這世上最難降的妖魔是什么子漩? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任豫喧,我火速辦了婚禮,結果婚禮上幢泼,老公的妹妹穿的比我還像新娘紧显。我一直安慰自己,他們只是感情好缕棵,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布孵班。 她就那樣靜靜地躺著,像睡著了一般招驴。 火紅的嫁衣襯著肌膚如雪篙程。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天别厘,我揣著相機與錄音虱饿,去河邊找鬼。 笑死触趴,一個胖子當著我的面吹牛氮发,可吹牛的內容都是我干的。 我是一名探鬼主播冗懦,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爽冕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了披蕉?” 一聲冷哼從身側響起扇售,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤前塔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后承冰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體华弓,經...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年困乒,在試婚紗的時候發(fā)現(xiàn)自己被綠了寂屏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡娜搂,死狀恐怖迁霎,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情百宇,我是刑警寧澤考廉,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站携御,受9級特大地震影響昌粤,放射性物質發(fā)生泄漏。R本人自食惡果不足惜啄刹,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一涮坐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧誓军,春花似錦袱讹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壹甥,卻和暖如春救巷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盹廷。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工征绸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留久橙,地道東北人俄占。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像淆衷,于是被迫代替她去往敵國和親缸榄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355