@selector詳解(轉)(正在理解)


@selector 是什么?

1一種類型 SEL
2代表你要發送的消息(方法), 跟字符串有點像, 也可以互轉.: NSSelectorFromString()   /   NSSelectorFromString()

3可以理解為類似函數指針的東西--是能讓Objective-C動態調用方法的玩意.--是 object-c 的動態后綁定技術 可以通過字符串 訪問的函數指針
4其實就是消息響應函數---選一個消息響應的函數地址給你的action
5@selector(function_name) 即取得一個function的id

objc_msgxxx 系列函數是全局的
performSelector 是NSObject成員方法,ms效果差不多

關於objc_msgSend & performSelector系列函數的問題。

1。objc_msgSend:
書上說這個函數是OC編譯器在編譯的時候,遇到類似[object foo]的寫法時,就會把相應OC的語法轉成C的objc_msgSend函數了。還有相應的objc_msgSendSuper


2。關於performSelector系列:
performSelector, performSelector:withDelay:, performSelector:withObj:withDelay, XXX...
除了第一個,后面幾個都可以指定一個延遲時間,然后就把selector放到當前線程的runloop中等待調用。從方法名上看,感覺performSelector == performSelector:withDelay系列中,delay為0的情況。


我的問題是:
1。objc_msgSend這個函數是直接由[object foo]轉過來的嗎? 也就是說,中間不會轉成performSelector吧?objc_msgSend是同步的嗎?
2。performSelector:withDelay:這個是異步的了,放進runloop中,那如果performSelector和performSelector:withDelay(delay為0時)一樣的話, 那performSelector也是異步的嗎?也要進runloop嗎?


剛開始看cocoa,有很多不懂的。

 

tianya 2011-05-11 08:13
答:
1、是的,不要performSelector了,直接objc_msgSend。事實上,這個是OC編譯時就轉好的,也就是OC編譯的時候就把對象調用轉成函數的call了
2、是的,delay為0也進runloop,這樣做的好處是可以不阻住當前調用performSelector:withDelay:的方法的執行

怎樣證實上邊的答案沒有問題?
很簡單,寫幾個測試代碼,然后打斷點一跑,在運行時看調用棧。
觀察程序運行最好的方法總是調試器~

respondsToSelector

3、delegate屬性使用assign的原因。

循環引用

所有的引用計數系統,都存在循環應用的問題。例如下面的引用關系:

對象a創建並引用到了對象b.

對象b創建並引用到了對象c.

對象c創建並引用到了對象b.

這時候b和c的引用計數分別是2和1。當a不再使用b,調用release釋放對b的所有權,因為c還引用了b,所以b的引用計數為1,b不會被釋放。b不釋放,c的引用計數就是1,c也不會被釋放。從此,b和c永遠留在內存中。

這種情況,必須打斷循環引用,通過其他規則來維護引用關系。比如,我們常見的delegate往往是assign方式的屬性而不是retain方式的屬性,賦值不會增加引用計數,就是為了防止delegation兩端產生不必要的循環引用。如果一個UITableViewController 對象a通過retain獲取了UITableView對象b的所有權,這個UITableView對象b的delegate又是a,如果這個delegate是retain方式的,那基本上就沒有機會釋放這兩個對象了。自己在設計使用delegate模式時,也要注意這點。

因為循環引用而產生的內存泄露也是Instrument無法發現的,所以要特別小心。

4、delegate屬性使用assign的原因。

還有一些用法會讓系統擁有對象的所有權。比如NSObject 的performSelector:withObject:afterDelay 。如果有必要,需要顯示的調用cancelPreviousPerformRequestsWithTarget:selector:object: ,否則有可能產生內存泄露。

 

iPhone開發中,動態調用類和方法:

NSClassFromString

NSSelectorFromString

 

正常來說,

id myObj = [[NSClassFromString(@"MySpecialClass") alloc] init];

id myObj = [[MySpecialClass alloc] init];

是一樣的。但是,如果你的程序中並不存在MySpecialClass這個類,下面的寫法會出錯,而上面的寫法只是返回一個空對象而已。

因此,在某些情況下,可以使用NSClassFromString來進行你不確定的類的初始化。

比如在iPhone中,NSTask可能就會出現這種情況,所以在你需要使用NSTask時,最好使用:

[[NSClassFromString(@"NSTask") .....]]

而不要直接使用[NSTask ...]這種寫法。

NSClassFromString的好處是:

1 弱化連接,因此並不會把沒有的Framework也link到程序中。

2 不需要使用import,因為類是動態加載的,只要存在就可以加載。

 

 

[cpp]  view plain copy
 
    1. for (int c=0; c<[classNames count]; c++) {  
    2. NSString *className=[classNames objectAtIndex:c];  
    3. id class=[[NSClassFromString(className) alloc] init];  
    4. for (int i=0; i<[params count]; i++) {  
    5. [class performSelector:NSSelectorFromString([NSString stringWithFormat:@"setA%i",i])];  
    6. }  
    7. }  
    8.          
    9.                                   object c中的selector



      其作用相當於函數指針,現在我看到的大多說用法都是在調用某些函數需要傳遞一個 函數指針 參數時,使用@selector。它會在當前類里面查找selector后面所跟的函數,返回一個SEL類型的值。
      SEL變量的執行.用performSelecor方法來執行. 
      [對象 performSelector:SEL變量 withObject:參數1 withObject:參數2]; 
      在調用respondsToSelector:@selector(method)時,這個method只有在該方法存在參數時需要 ":",如果該方法不需要參數就不需要加這個冒號。否則,編譯不會報錯,只是執行返回的值不對。當然如果方法有多個參數,需要多個冒號,參數有名稱的需要帶上參數名稱。 
      如:有如下方法:
      -(NSString*)toXmlString;
      此時調用類似於:
      [self respondsToSelector:@selector(toXmlString)]
      如果toXmlString方法的定義為:
      -(NSString*)toXmlString:(NSString*)prefix;
      那么調用就必須加上冒號,如:[self respondsToSelector:@selector(toXmlString:)]
      ·-(BOOL) isKindOfClass: classObj 用來判斷是否是某個類或其子類的實例 
      ·-(BOOL) isMemberOfClass: classObj 用來判斷是否是某個類的實例 
      ·-(BOOL) respondsToSelector: selector 用來判斷是否有以某個名字命名的方法(被封裝在一個selector的對象里傳遞) 
      ·+(BOOL) instancesRespondToSelector: selector 用來判斷實例是否有以某個名字命名的方法. 和上面一個不同之處在於, 前面這個方法可以用在實例和類上,而此方法只能用在類上. 
      ·-(id) performSelector: selector 執行某個方法




      [Objective-C]SEL類型、@selector選擇器 


      1     id cattle[3]; 
      2     SEL say; 
      3     SEL skin; 

        其中id cattle[3]定義了一個數組用於存儲Cattle或者Bull對象。這一行代碼估計大家都很熟悉,筆者就不贅述了。像這樣的傳統的數組並不能完全滿足我們的需求,當我們需要做諸如追加,刪除等操作的時候,會很不方便。在隨后的章節里面筆者將要向大家介紹傳統數組的替代解決方案NSArray。 

        上一段代碼的第二行和第三行是本節所關注的,就是SEL類型。Objective-C在編譯的時候,會根據方法的名字(包括參數序列),生成一個用 來區分這個方法的唯一的一個ID,這個ID就是SEL類型的。我們需要注意的是,只要方法的名字(包括參數序列)相同,那么它們的ID都是相同的。就是 說,不管是超類還是子類,不管是有沒有超類和子類的關系,只要名字相同那么ID就是一樣的。除了函數名字和ID,編譯器當然還要把方法編譯成為機器可以執 行的代碼,這樣,在一個編譯好的類里面,就產生了如下圖所示方法的表格示意圖(本構造屬於筆者推測,沒有得到官方證實,所以圖5-2為示意圖僅供參考,我們可以暫時認為是這樣的)。 

      Objective-C學習---------SEL類型  
      圖5-2,方法的表格示意圖 

        請注意setSkinColor后面有一個冒號,因為它是帶參數的。由於存在這樣的一個表格,所以在程序執行的時候,我們可以方便的通過方法的名字,獲取到方法的ID也就是我們所說的SEL,反之亦然。具體的使用方法如下: 


      1     SEL 變量名 = @selector(方法名字); 
      2     SEL 變量名 = NSSelectorFromString(方法名字的字符串); 
      3     NSString *變量名 = NSStringFromSelector(SEL參數); 


      其中第1行是直接在程序里面寫上方法的名字,第2行是寫上方法名字的字符串,第3行是通過SEL變量獲得方法的名字。我們得到了SEL變量之后,可以通過下面的調用來給一個對象發送消息: 

        [對象 performSelector:SEL變量 withObject:參數1 withObject:參數2]; 

        這樣的機制大大的增加了我們的程序的靈活性,我們可以通過給一個方法傳遞SEL參數,讓這個方法動態的執行某一個方法;我們也可以通過配置文件指定需要執行的方法,程序讀取配置文件之后把方法的字符串翻譯成為SEL變量然后給相應的對象發送這個消息。 

        從效率的角度上來說,執行的時候不是通過方法名字而是方法ID也就是一個整數來查找方法,由於整數的查找和匹配比字符串要快得多,所以這樣可以在某種程度上提高執行的效率。

       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM