@selector 是什么?
1一種類型 SEL
2代表你要發送的消息(方法), 跟字符串有點像, 也可以互轉.: NSSelectorFromString() / NSSelectorFromString()
3可以理解為類似函數指針的東西--是能讓Objective-C動態調用方法的玩意.--是 object-c 的動態后綁定技術 可以通過字符串 訪問的函數指針
4其實就是消息響應函數---選一個消息響應的函數地址給你的action
5@selector(function_name) 即取得一個function的id
objc_msgxxx 系列函數是全局的
performSelector 是NSObject成員方法,ms效果差不多
|
||
|
||||
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,因為類是動態加載的,只要存在就可以加載。
- for (int c=0; c<[classNames count]; c++) {
- NSString *className=[classNames objectAtIndex:c];
- id class=[[NSClassFromString(className) alloc] init];
- for (int i=0; i<[params count]; i++) {
- [class performSelector:NSSelectorFromString([NSString stringWithFormat:@"setA%i",i])];
- }
- }
-
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為示意圖僅供參考,我們可以暫時認為是這樣的)。
圖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也就是一個整數來查找方法,由於整數的查找和匹配比字符串要快得多,所以這樣可以在某種程度上提高執行的效率。
