iOS開發中@selector的理解與應用

@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嗎?
答:
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,因爲類是動態加載的,只要存在就可以加載。

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])];
}
}

發佈了3 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章