iOS SEL(@selector)原理

SEL 類成員方法的指針

可以理解 @selector()就是取類方法的編號,他的行爲基本可以等同C語言的中函數指針,只不過C語言中,可以把函數名直接賦給一個函數指針,而Object-C的類不能直接應用函數指針,這樣只能做一個@selector語法來取.

它的結果是一個SEL類型。這個類型本質是類方法的編號(函數地址)

C/C++函數指針


int test(int val)

{
return val+1;

}

int (* c_func)(int val); //定義一個函數指針變量c_func = add ; //把函數addr地址直接賦給c_func


object-c的選擇器,

@interface foo
-(int)add:int val;

@end

SEL class_func ; //定義一個類方法指針class_func = @selector(add:int);

注意1、@selector是查找當前類(含子類)的方法。

舉例:

父類.h文件

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface SelectorDemo : NSObject  
  4. {  
  5.     SEL _methodTest;  
  6. }  
  7.   
  8. @property (nonatomic,assign) SEL methodTest;//這裏聲明爲屬性方便在於外部傳入。  
  9.   
  10. -(void)TestParentMethod;  
  11.   
  12. -(void)TestSubMethod;  
  13.   
  14.   
  15. @end  
.m文件

[plain] view plaincopy
  1. #import "SelectorDemo.h"  
  2.   
  3. @implementation SelectorDemo  
  4.   
  5. @synthesize methodTest = _methodTest;  
  6.   
  7. -(void)parentMethod  
  8. {  
  9.     NSLog(@"parent method Call Success!");  
  10. }  
  11.   
  12. -(void)TestParentMethod  
  13. {  
  14.     if (_methodTest)  
  15.     {  
  16.         [self performSelector:_methodTest withObject:nil];  
  17.     }  
  18. }  
  19.   
  20. -(void)TestSubMethod  
  21. {  
  22.     if (_methodTest)  
  23.     {  
  24.         [self performSelector:_methodTest withObject:nil];  
  25.     }  
  26. }  
  27.   
  28. @end  

子類:

.h文件

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2. #import "SelectorDemo.h"  
  3.   
  4.   
  5. @interface SelectorSub : SelectorDemo  
  6.   
  7.   
  8. @end  
.m文件

[plain] view plaincopy
  1. #import "SelectorSub.h"  
  2.   
  3.   
  4. @implementation SelectorSub  
  5.   
  6. -(void)SubMethod  
  7. {  
  8.     NSLog(@"Sub method Call Success!");  
  9. }  
  10.   
  11.   
  12. @end  

進行測試調用。
[plain] view plaincopy
  1. SelectorSub *ss = [[SelectorSub alloc]init];  
  2.     ss.methodTest = @selector(parentMethod);  
  3.     [ss TestParentMethod];  
  4.     ss.methodTest = @selector(SubMethod);  
  5.     [ss TestParentMethod];  
  6.     [ss release];  

ss.methodTest = @selector(parentMethod); 這句在運行期時,會尋找到父類中的方法進行調用。

ss.methodTest = @selector(SubMethod);//這句就在運行期時,會先尋找父類,如果父類沒有,則尋找子類。


如果這裏將ss.methodTest = @selector(test); 其中test即不是ss父類,也不是ss本身,也非SS子類,哪麼這個時候在使用

[self performSelector:_methodTest withObject:nil];就會出現地址尋找出錯 。


  1. 下面的其實是很好的解釋爲什麼必須是自身類或者子類。  
  1. [friend performSelector:@selector(gossipAbout:) withObject:aNeighbor];  
等價於:
  1. [friend gossipAbout:aNeighbor];  
  1.   
  1. 通過這個原理,當把屬性設置爲SEL類型時,如果回調機制使用的不是SEL聲明的類或子類。想實現其它類的回調,必須傳入其它類的上下文句柄。  
  1. 舉例:  
  1. 上面的SelectorDemo 類修改爲:  

[plain] view plaincopy
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface SelectorDemo : NSObject  
  4. {  
  5.     SEL _methodTest;  
  6.     id _handle;  
  7. }  
  8.   
  9. @property (nonatomic,assign) SEL methodTest;  
  10. @property (nonatomic,retain) id handle;        //添加其它類的實例句柄屬性。  
  11. -(void)TestParentMethod;  
  12. -(void)TestSubMethod;  
  13.   
  14. @end  
[plain] view plaincopy
  1. #import "SelectorDemo.h"  
  2.   
  3. @implementation SelectorDemo  
  4.   
  5. @synthesize methodTest = _methodTest;  
  6. @synthesize handle = _handle;  
  7.   
  8. -(void)parentMethod  
  9. {  
  10.     NSLog(@"parent method Call Success!");  
  11. }  
  12.   
  13. -(void)TestParentMethod  
  14. {  
  15.     if (_methodTest)  
  16.     {  
  17.         [_handle performSelector:_methodTest withObject:nil];//這裏面原來self屬爲相應的實例句柄  
  18.     }  
  19. }  
  20.   
  21. -(void)TestSubMethod  
  22. {  
  23.     if (_methodTest)  
  24.     {  
  25.         [_handle performSelector:_methodTest withObject:nil];  
  26.     }  
  27. }  
  28.   
  29. @end  

到這裏我想熟悉IOS,target-action模式的,都清晰了。

Target-Action設計模式

在處理用戶-接口控件方面,AppKit充分發揮了在運行時改變接收者和消息的能力。

NSControl對象是一個圖形設備,可以用來嚮應用程序發送指令,。大多實現了現實世界中的控制裝置,例如button、switch、knob、text field、dial、menu item等。在軟件中,這些設備處於用戶和和應用程序之間。它們解釋來自硬件設備,如鍵盤和鼠標的事件,並將它們翻譯成應用程序特定的指令。例如,名爲“Find”的按鈕將會把鼠標點擊事件翻譯成開始搜索的應用程序指令。

AppKit爲創建控件設備定義了模板,並定義了一些自己的現成設備。例如,NSButtonCell類定義了一個對象,可以指派給一個NSMatrix實例,並初始化它的大小、名稱、圖片、字體和鍵盤快捷鍵。當用戶點擊按鈕(或使用鍵盤快捷鍵)時,NSButtonCell對象發送消息,指示應用程序工作。爲此,NSButtonCell對象不僅要初始化圖像、大小和名稱,還要確定消息要發往何方和發給誰。相應地,NSButtonCell實例可以爲一個action消息(它將在自己發送的消息中使用的對象選擇器)和一個target(接收該消息的對象)進行初始化。

  1. [myButtonCell setAction:@selector(reapTheWind:)];  
  2. [myButtonCell setTarget:anObject];  
當用戶點擊了相應的按鈕,該按鈕單元將使用NSObject協議方法performSelector:withObject:發送消息。所有action消息帶有單獨一個參數,既發送該消息的控件設備的id。

如果Objective-C不允許改變消息,所有的NSButtonCell對象將不得不發送相同的消息,方法的名字將在NSButtonCell源代碼中寫死。與簡單的實現將用戶action轉換爲action消息的機制不同,按鈕單元和其他控件不得不限制消息的內容。受限的消息會使很多對象難以響應多於一個的按鈕單元。要麼每個按鈕有一個target,要麼target對象能發現消息來自於那個按鈕,並做相應處理。每次在重新佈局用戶接口時,你也必須實現響應action消息的方法。動態消息的缺乏將會帶來不必要的麻煩,但Objective-C很好地避免了這一點。


從前面的例子可以得知如果SEL不是自身的方法,在調用時就會出錯,引起CRASH,哪麼如何避免消息傳遞引起的錯誤。見下文章:

避免消息錯誤

如果一個對象接收了一條消息去執行不歸它管的方法,就會產生錯誤結果。這和調用一個不存在的函數是同一類錯誤。但是,因爲消息發生在運行時,錯誤只有在程序執行後纔會出現。

當消息選擇器是常數並且接收對象類已知時,處理這種錯誤相對容易。在寫程序時,你可以確保接收者能夠響應。如果接收者時靜態類型,編譯器將替你完成該測試。

但是,如果消息選擇器或接收者是變化的,那麼只能在運行時進行相關測試。NSObject類中定義的respondsToSelector:方法可以測試一個接收者是否能夠響應某條消息。它將方法選擇器作爲參數並返回接收者是否已經訪問了與選擇器相匹配的一個方法:

  1. if ( [anObject respondsToSelector:@selector(setOrigin::)] )  
  2.     [anObject setOrigin:0.0 :0.0];  
  3. else  
  4.     fprintf(stderr, "%s can’t be placed\n",  
  5.         [NSStringFromClass([anObject class]) UTF8String]);  
當你向一個你在編譯時無法控制的對象發送消息時,respondsToSelector:運行時測試非常重要。例如,如果你寫了一段代碼向一個對象發送消息,而這個對象是一個他人可以設定值的變量,那麼你就要確保接收者實現了響應該消息的方法。

注意:一個對象在收到不是自己負責直接響應的消息時可以轉發該消息給其他對象。這種情況下,從調用者的角度來看,對象直接處理了消息,儘管該對象是通過轉發消息給其他對象來處理的。



注意2、查找類方法時,除了方法名,方法參數也查詢條件之一.

這個主要是多個參數時需要注意,如:

  1. SEL setWidthHeight;  
  2. setWidthHeight = @selector(setWidth:height:);  


注意3、可以用字符串來找方法 SEL 變量名 = NSSelectorFromString(方法名字的字符串);

注意4、 可以運行中用SEL變量反向查出方法名字字符串,如:NSString *method = NSStringFromSelector(setWidthHeight);

注意5、SEL 查找的方法不支持類方法(即靜態方法,在C++中帶static關鍵字的,在OBJECT-C中即方法前帶+號的,DELPHI中爲class function)。

持續學習中!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章