1. 程式人生 > >runtime總結二之訊息機制(包括訊息轉發,訊息交換的黑魔法)

runtime總結二之訊息機制(包括訊息轉發,訊息交換的黑魔法)

runtime的訊息機制

前面提到過編譯器最終會把我們的訊息傳送轉化為函式呼叫

  • 訊息傳送 [object sendMassage]
    首先編譯器會在執行時將上面的例子轉化為objc_msgSend(obj,@selector(sendMassage))這個函式,轉換的時候除了方法本身的引數之外,還有兩個隱藏的引數一個是id型別的,代表物件的型別,還是一個是SEL型別的,是函式對應的方法的編號,接下來就會按照下面的流程來呼叫這個方法
  1. 通過obj的isa指標找到其所對應的類。
  2. 通過SEL先去類的cache列表中找這個方法,如果就去找方法的實現,不存在,進入第3步
  3. 去類的method列表中找,如果就去找方法的實現,沒有找到,根據類中的superclass指標去父類中找,一直到NSObject.
找到了方法之後就要去找方法的實現,那麼如何找方法的實現呢,runtime提供了兩種方式
IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m)
在id objc_msgSend(id self, SEL _cmd, ...) {
  Class class = object_getClass(self);
  IMP imp = class_getMethodImplementation(class, _cmd);
  return imp ? imp(self, _cmd, ...
) : 0; }

可以看到這個方法中的第二行程式碼imp,可以通過這個imp來查詢這個方法的實現,要是沒有找到,runtime給我們提供了三次機會讓我們的程式不會崩潰,也就是下面要提到的動態方法解析和訊息轉發(訊息重定向,訊息轉發)
* 動態方法解析
resolveInstanceMethod或者resolveClassMethod給你提供一次新增方法實現的機會
下面例子,在student類中有一個沒有實現的write方法,但是沒有實現,如果我們掉用它,會因為找不到實現程式崩潰,有下面的挽救措施

//在這個c語言函式新增方法的實現
void test(){
    NSLog(@"我是動態新增的方法"
); } //然後實現這個方法,當這個方法返回no時或者沒有實現時會進入訊息轉發 +(BOOL)resolveInstanceMethod:(SEL)sel{ if (sel == @selector(write)) { class_addMethod([self class], sel, (IMP)test, "[email protected]:"); } return [super resolveInstanceMethod:sel]; }

如你添加了函式實現並且返回了yes,那麼就會呼叫。不會進入下面的訊息轉發,否則進入下面的訊息轉發

  • 訊息轉發
訊息轉發分為兩步,一次是訊息的重定向,返回一個實現了該方法的物件,還有一次是真正的轉發,是把這個函式的引數及相關資訊打包成一個物件,把他傳送給另外一個類,讓他去處理(PS:訊息重定向是返回一個提供了實現的物件,訊息轉發是將方法引數打包到一個物件裡面,然後把這個物件傳送出去)。
  1. 訊息重定向:需要實現這個函式- (id)forwardingTargetForSelector:(SEL)aSelector,通過這個函式返回一個實現了該方法的物件。如果返回了self或者no的時候,就會進入訊息轉發,否則不會

定義一個新類,裡面新增一個和student中未實現的方法同名的方法,而且這個方法有實現,假設叫testStudent,然後在student中實現下面的方法

//該方法返回一個添加了方法實現的物件,
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(write)) {
    //studentTest類中這個方法有實現
        StudentTest *test = [[StudentTest alloc]init];
        return test;
    }
    return [super forwardingTargetForSelector:aSelector];
}
  1. 訊息轉發:需要實現兩個方法
    methodSignatureForSelector:返回一個NSInvocation*的物件,將方法的引數返回值封裝在裡面。
    - (void)forwardInvocation:(NSInvocation *)anInvocation,將該物件傳送給一個提供了方法實現的物件。
    和上面的訊息重定向一樣,只不過student中要實現的方法有了差別
//這個方法返回一個NSInvocation*的物件,裡面打包了有關於這個未實現方法的資訊
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSString*sel = NSStringFromSelector(aSelector);
    if ([sel isEqualToString:@"write"]) {
        return [NSMethodSignature signatureWithObjCTypes:"[email protected]:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

-(void)forwardInvocation:(NSInvocation *)anInvocation{
    SEL selector = [anInvocation selector];
    StudentTest *test = [[StudentTest alloc]init];
    if ([test respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:test];
    }
}

總結一下

傳送一個訊息:底部會轉成objc_msgSend函式,這個函式除了方法的引數之外還有兩個隱藏的引數self和_cmd,接下來就會按照下面的流程去呼叫這個函式

  1. 根據isa指標找到物件所屬的類或者類所屬的元類
  2. 先去類或者元類的cache列表中根據SEL去找這個方法。
  3. 沒有找到,去method方法列表中找
  4. 還是沒有找到,就去父類中找
  5. 找到了,根據SEL找到對應的IMP,呼叫這個函式
  6. 沒有找到,進入動態方法解析或者訊息轉發。

動態方法解析:

runtime提供的第一次實現這個方法的機會,要實現resolveInstanceMethod/resolveClassMethod方法,給未實現的方法在執行時新增實現,返回no/不實現,進入訊息轉發。

訊息轉發一:訊息重定向

訊息重定向:返回一個實現了該方法的物件,要實現-(id)forwardingTargetForSelector:(SEL)aSelector函式,如果返會nil/self,則進入下面的訊息轉發二

訊息轉發二:訊息轉發

將未實現方法的相關資訊打包成一個NSInvocation物件,然後交給一個類去實現。需要實現-(NSMethodSignature )methodSignatureForSelector:(SEL)aSelector和-(void)forwardInvocation:(NSInvocation )anInvocation方法

疑問?為什不直接找IMP而要通過SEL這個中間人呢?

SEL只是方法的編號,真是的實現是通過IMP來查詢的,SEL和IMP之間是一一對映的關係,通過SEL我們可以改變他的IMP,然後讓一個方法在不同的情況下有不同的實現,例如實現方法的交換,有時候我們需要給系統的方法新增一些自己的東西
1:可以通過一個子類繼承於系統類,然後重寫那個類的方法
2:通過分類,但是會覆蓋系統的方法
3:寫一個自己的方法,通過runtime在load方法中交換系統方法和自己的方法的實現
下面主要針對第三種例子舉例:
第一步首先為我們要動手腳的系統方法類新增一個分類,
假設我們要為imageNamed新增一個判斷nil的功能,先要為他新增一個分類,然後給系統的imageNamed方法新增字首,明明一個自己的方法,如下
分類的h檔案
這裡寫圖片描述
分類的m檔案,對於這裡乍一看可能像遞迴,其實在第一次呼叫的系統的imageNamed方法時呼叫的是my_imageNamed方法,當第呼叫的my_imageNamed方法時其實在呼叫系統的imageNamed方法

交換
這裡寫圖片描述
至於為什麼要在load方法中寫,我會在別的部落格中提到。

相關推薦

runtime總結訊息機制包括訊息轉發訊息交換魔法

runtime的訊息機制 前面提到過編譯器最終會把我們的訊息傳送轉化為函式呼叫 訊息傳送 [object sendMassage] 首先編譯器會在執行時將上面的例子轉化為objc_msgSend(obj,@selector(sendMassage))這個

我的CUDA學習旅1——大影象分塊處理程式包括求均值最大值等

引言 在我的第一篇文章中我簡單介紹了CUDA以及我的一些個人學習見解,在本文中我將開始正式開始CUDA實踐之旅,眾做周知CUDA目前應用的領域十分廣泛,它能把一些普通的CPU程式碼提速幾十倍甚至幾百倍。在本人所從事的影象處理領域,在一些大影象的處理上(4K以上

設計模式單利模式C#語言描述附視訊下載地址

今天來介紹所有設計模式中結構最簡單的設計模式單例模式,它的核心結構中只包含一個被稱為單例類的特殊類。 要想完成單例類的設計,我們要遵循一下原則即可: 1、一個類只能有一個例項 2、確保該例項對外有一個訪問入口(保證我們的系統可以從這個入口拿到該類的唯一例項) 3、將單例類的建構函式私有化(private),當

JDBC對事務的使用包括自動提交回滾等知識

首先得清楚什麼時候使用事務。    當你需要一次執行多條SQL語句時,可以使用事務。通俗一點說,就是,如果這幾條SQL語句全部執行成功,則才對資料庫進行一次更新,如果有一條SQL語句執行失敗,則這幾條SQL語句全部不進行執行,這個時候需要用到事務。    其次才是事務的具體使

WPF的訊息機制- WPF內部的5個視窗隱藏訊息視窗

原文: WPF的訊息機制(二)- WPF內部的5個視窗之隱藏訊息視窗   目錄 WPF的訊息機制(一)-讓應用程式動起來 WPF的訊息機制(二)-WPF內部的5個視窗 (1)隱藏訊息視窗 (2)處理啟用和關閉的訊息的視窗和系統資源通知視窗 (3)用於

WPF的訊息機制- WPF內部的5個視窗處理啟用和關閉的訊息視窗以及系統資源通知視窗

原文: WPF的訊息機制(三)- WPF內部的5個視窗之處理啟用和關閉的訊息視窗以及系統資源通知視窗 目錄 WPF的訊息機制(一)-讓應用程式動起來 WPF的訊息機制(二)-WPF內部的5個視窗 (1)隱藏訊息視窗 (2)處理啟用和關閉的訊息視窗和系統資源通知視窗

Windows程式和訊息機制訊息有關的函式

不同視窗程式可以通過訊息進行互動,主要用到的函式如下: FindWindow 獲取一個視窗的控制代碼。 HWND FindWindow( LPCTSTR lpClassName,// 類名 LPCTSTR lpWindowName//

Android訊息機制 ThreadLocal

前言   今天重溫了Android開發藝術探索上的訊息機制,花了一些時間,書上寫很好,但是可能文章一些先後順序問題,不是特別好理解,這篇文章博主用了自己的理解,看原始碼,結合書上的知識,希望大家能更容易理解。(可能會寫的不太好。。。不正確的地方歡迎指出)  

類加載初始化包括靜態代碼塊講解

round ima left line title 耗時 觸發 靜態代碼塊 cnblogs 開始我們先來看一段代碼 package classLoader; class a { public a() { System.out.println

刷題總結——逼平衡樹bzoj3224線段樹套splay

++ output 數列 turn 表示 roo string 修改 和我 題目: Description 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:1.查詢k在區間內的排名2.查詢區間內排名為k的值3.修改某一位值上的數值4.查詢

使用truelicense實現用於JAVA工程license機制包括license生成和驗證

參數 cat this false sem inf import 存儲密碼 dos命令 開發的軟件產品在交付使用的時候,往往會授權一段時間的試用期,這個時候license就派上用場了。不同於在代碼中直接加上時間約束,需要重新授權的時候使用license可以避免修改源碼,改

圖論學習Topological Sort拓撲排序

src info directed com 遞歸 ica -- 遞歸版 拓撲       拓撲排序 Topological-Sort• 對一個有向無環圖G進行 拓撲排序, 是將G中所有 頂點排成一個線性序列, 使 對 於 圖 中 任 意 弧 <u,

Java動態性反射機制reflection

clas nts 一次 完全 名稱 set 靜態類 對象實例 以及 說到反射機制,第一次接觸的人可能會比較懵,反射?什麽反射?怎麽反射?反射是幹嘛的?下面我將在這篇文章中講講Java的反射機制 不過在這之前,還有一個問題需要解決,標題名中的動態性,說起動態性,我先介紹介紹動

WPF的訊息機制- 讓應用程式動起來

原文: WPF的訊息機制(一)- 讓應用程式動起來 前言 談起“訊息機制”這個詞,我們都會想到Windows的訊息機制,系統將鍵盤滑鼠的行為包裝成一個Windows Message,然後系統主動將這些Windows Message派發給特定的視窗,實際上訊息是被Post到特定視窗所線上程

java IO 位元組流、字元流操作總結位元組流

上一篇,主要介紹了檔案類File和RandomAccessFile類的用法。接下來,我覺得還是將IO流分為位元組流和字元流兩部分介紹比較好。這樣不至於搞混亂,同時也便於對比。這一篇主要介紹位元組流。 1、位元組流 首先上一張位元組流的家族圖譜。 位元組流主要分為兩部分:InputS

Spring知識點總結()Spring IOC

1.建立bean類,並在spring中進行配置交由spring來管理1. IOC(DI) - 控制反轉(依賴注入)    所謂的IOC稱之為控制反轉,簡單來說就是將物件的建立的權利及物件的生命週期的管理過程交由Spring框架來處理,從此在開發過程中不再需要關注物件的建立和生命週

Windows程式和訊息機制訊息與程序間通訊

自定義訊息與程序間通訊 視窗程式可以接收自定義的訊息型別,前提是通訊的程序聲明瞭這種訊息型別,宣告的方法很簡單,WM_USER加一個值就可以了,一般加的值從0x400開始,其他的值已經被系統使用了。 實現一個完整的自定義訊息需要進行以下步驟:

Mybatis學習總結基於 XML 的單表CRUD操作

上一篇我們講了MyBatis配置檔案中的配置及其優化:https://blog.csdn.net/qq_38720976/article/details/84484034 本文將通過專案使用MyBatis對錶執行CRUD操作  mybatis-3.3.0.jar下載路徑:ht

29.分支篇VPN部署包括對接、雙鏈路冗餘、優化與分析、策略路由與NAT的影響

拓撲 拓撲可以儲存到本地,然後擴大檢視,這樣才能看的更清楚。(拖動到新視窗開啟即可) 路由器配置VPN,實現財務部門互訪,並且AP能夠正常關聯到總部AC。 3.1、建立環回口 [GW]interface lo0 [GW-LoopBack0]ip address 2.

Android訊息機制Handler、MessageQueue和Looper三者的工作原理

Android的訊息機制主要是指Handler的執行機制以及Handler所附帶的MessageQueue和Looper的工作過程。messagequeue意思是訊息佇列,它內部儲存一組訊息,有插入和刪除的功能,其實內部是以單鏈表的形式來實現佇列功能的。looper的意思是迴圈,它的主要功能是迴