IOS SEL (@selector) 原理及使用總結(二)

來源:互聯網
上載者:User

標籤:blog   http   io   ar   color   os   使用   sp   for   

SEL訊息機制工作原理是什麼 

引用下面文章:

 

我們在之前有提到,一個類就像一個 C 結構.NSObject 聲明了一個成員變數: isa. 由於 NSObject 是所有類的根類,所以所有的對象都會有一個 isa 的成員變數[公用繼承].而該 isa 變數指向該對象的類(圖3.15)[類在Objective-C中也是一個實體, 由於存在Objective-C 運行環境所有的類將有自己的儲存空間.Objective-C 運行環境將為每個類分配空間. 這裡 所說的 isa,正是指向這樣一個類的空間. 從而建立類和對象之間的對應關係.] 類空間 包含了該類定義的成員變數,以及方法實現, 還包含了指向自己父類空間的指標. 

 

 

方法以 selector 作為索引. selector 的資料類型是 SEL. 雖然 SEL 定義成 char*, 我們可 以把它想象成 int. 每個方法的名字對應一個唯一的 int 值.比如, 方法 addObject: 可能 對應的是 12. 當尋找該方法是, 使用的是 selector,而不是名字 @"addObject:"

Objective-C 資料結構中,存在一個 name - selector 的映射表 3.16 

 

 

在編譯的時候, 只要有方法的調用, 編譯器都會通過 selector 來尋找,所以 (假設 addObject 的 selector 為 12)

 [myObject addObject:yourObject]; 

將會編譯變成

objc_msgSend(myObject, 12, yourObject);

這裡,objec_msgSend()函數將會使用 myObjec 的 isa 指標來找到 myObject 的類空間結構並 在類空間結構中尋找 selector 12 所對應的方法.如果沒有找到,那麼將使用指向父類的指 針找到父類空間結構進行 selector 12 的尋找. 如果仍然沒有找到,就繼續往父類的父類一 直找,直到找到為止, 如果到了根類 NSObject 中仍然找不到,將會拋出異常.

我們可以看到, 這是一個很動態尋找過程.類的結構可以在啟動並執行時候改變,這樣可以很 容易來進行功能擴充[Objective-C 語言是動態語言, 支援動態綁定.

 

再來看

Objective-C如何擷取訊息的原理詳解文章

Objective-C擷取訊息工作原理是本文要介紹的內容,看name mangling的時候,也講到了Objective-C的name mangling,於是又重新讀了一下Objective-C 2.0 programming Language以及Objective-C 2.0 Runtime Reference裡的相關內容,自己歸納一下。

先貼一段代碼:

 

[plain] view plaincopy
  1. MyClass.h    
  2.    @interface MyClass : NSObject    
  3.    {    
  4.     }    
  5.    @end    
  6.    MyClass.m    
  7.    #import    
  8.    #import “MyClass.h”    
  9.    void myClassIMP(id _rec, SEL _cmd, int theInt)    
  10.    {    
  11.       NSLog(@”dynamic added method:%d”,theInt);    
  12.    }    
  13.       
  14. - (id)init    
  15. {    
  16.     if( ( self = [super init]) != nil )    
  17.     {    
  18.    class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”[email protected]:i”);    
  19.      }    
  20.     return self;    
  21. }    
  22.     
  23. Main.c    
  24. #import “MyClass.h”    
  25. int main(int argc, char *argv[])    
  26. {    
  27.   MyClass theInstance = [[MyClass alloc] init];    
  28.   [theInstance dynGeneratedMethod:10];    
  29.  return 0;    
  30. }  

這段代碼執行的結果是在控制台上輸出:

 

dynamic added method:10

 

接著來詳細分析一下上面的代碼:

在ObjC的類中這樣的一個聲明 – (void)foo:(int)a;被稱作方法(method),而在調用的地方: [theClass foo:10];則被稱之為發送訊息(send message),具體來說是給對象theClass 發送foo:訊息,注意這裡foo後面的”:”,它也是訊息名稱的一部分,最前面的‘-‘代表執行個體方法,‘+‘代表類方法。而類似的語句,在C或C++ 中,通常被稱為呼叫函數(call function),在ObjC中,函數(function)一詞很少用到,不是它不存在,而是它被ObjC runtime給隱藏了起來。

如前所述,ObjC是以訊息機制來工作的,但其實諸如-(void)foo:(int)a的語句在編譯時間被 objc_msgSend(receiver,selector,arg1,arg2,….)替換了,所以其實每一條發送訊息的代碼本質上還是調用函數 (call function),不過他們調用的都是同一個函數objc_msgSend(也可能是objc_msgSend_stret(傳回值是結構 體),objc_msgSend_fpret(傳回值是浮點型)等)

分析objc_msgSend的參數,第一個receiver的類型是id,代表接受訊息的對象,第二個是selector代表接收對象的方法,後面的是該方法的參數,之前那條語句的被編譯器替換後就是:

[theClass foo:10]  -> objc_msg(theClass,@selector(foo:),10);

 

 

因為訊息的接受對象和接受對象的方法都參數化,所以在運行時刻,接受對象和接受對象的方法都可以是動態!

比如說程式裡面可以這樣寫:

id helper = getTheReceiver();  SEL request = getTheSelector();  [helper performSelector:request];

它的實現是基於ObjC runtime. NSObject類實現了這套機制,所以每一個繼承於NSObject的類都能自動獲得runtime的支援。在這樣的一個類中,有一個isa指標,指向 該類定義的資料結構體,這個結構體是由編譯器編譯時間為類(須繼承於NSObject)建立的.在這個結構體中有包括了指向其父類類定義的指標以及 Dispatch table. Dispatch table是一張SEL和IMP的對應表。

對於名稱相同的方法,他們都有相同的SEL,方法的名稱不包括類名稱,所以子類和父類中的同名方法擁有相同的SEL,但是他們的實現可以各不相同, 因而在他們各自的Dispatch表中SEL所對應的IMP是不同的,IMP是一個函數指標,而雖然每一個SEL對應的是一個方法的名稱,但考慮到效 率,SEL本身是一個整型,編譯器會另外產生一張SEL和方法名稱對應的表。有了這樣的結構,objc就可以實現多態了。還是這行代碼:

[theClass foo:10];

是向theClass發送了foo:訊息,那麼首先在theClass的類結構的Dispatch table裡找有沒有對應的SEL,如果有的話,就表示theClass有響應該訊息的方法,程式就跳到該方法的代碼地址頭(由IMP指定),開始執行。 如果在theClass的Dispatch table找不到對應的SEL,那麼就會通過isa所指的結構體中包含的父類指標,到父類裡面去尋找,如果到最後還是沒有找到,就會出現runtime error.所以說,即使theClass以及它的父類都沒有定義-(void) foo:(int)a方法,程式還是可以通過編譯,但如果是用xcode的話,編譯器會有警告,告知theClass可能無法響應該訊息。不會報錯的原因 是類的方法也可以在執行時刻建立!上面的代碼:

class_addMethod([MyClassclass], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”[email protected]:i”);

就是給MyClass類在執行時刻增加了一個響應dynGeneratedMethod:訊息的方法,這樣之後對任何MyClass的instance類 發送dynGeneratedMethod:訊息,就會得到響應了.myClassIMP是類收到該訊息時要調用的方法,其聲明如下:

voidmyClassIMP(id _rec, SEL _cmd, inttheInt)

 

這個方法的前面兩個參數是必須的,之後的參數才是我們實際用到的參數,數目和@selector()中的冒號數一樣,冒號數代表的就是參數個數。第一個參數是訊息的接受對象,是MyClass的執行個體,第二個參數是由SEL代表的具體訊息。

Class_addMethod的最後一個參數是表示dynGeneratedMethod:的傳回值和參數資訊,不過我自己試了一下,這個參數不起作用。

幾個要點:

1、對於C中被稱為函數(function)和函數調用(function call)的地方,在ObjC中被叫做方法(method)和發送訊息(send message).試圖調用未定義的方法會導致編譯錯誤,而發送一條訊息,即使沒有任何類定義了響應該訊息的方法,編譯時間也不會報錯,從語義上講這也是對 的,發一條訊息本來就不要求一定有人會響應,不過如果執行到發送訊息的代碼時真的沒有類可以響應的話,是會發生runtime error,為了避免這種事情發生,可以先進行檢測,這樣寫

if ( [myClass respondsToSelector:@selector(foo:)])      [myClass foo:10];  }

我感覺ObjC這樣的一套sender receiver的定義更注重物件導向的概念。類是一個接收者(receiver),如果定義了某個方法,就可以接收和這個方法名稱相同的訊息。而使用該 類的client(sender),則嘗試向該類發送訊息.如果匹配了,就跳到類的方法裡執行。

2、方法名稱是諸如foo:,不包括傳回型別,參數類型,而又因為一個foo:對應於一個SEL,所以說ObjC不支援相同的foo:有不同的返回 類型,也不支援重載。不過類方法和執行個體方法可以有相同的名字,而又有不同類型的參數和傳回型別,因為它們不是處在同一張dispatch table中。

3、不僅類的方法可以運行時刻建立,類本身也可以在運行時刻建立,前面提到繼承於NSObject的類,編譯器會幫忙產生ObjC runtime所需要的類結構定義,只要我們在代碼裡也按照那個結構建立了自己的類,那一樣可以獲得ObjC runtime的支援。

 

 

 

http://blog.csdn.net/fengsh998/article/details/8614486

IOS SEL (@selector) 原理及使用總結(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.