前言
剛在群裡看到這樣一段程式碼,很有意思:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"2");
});
}
- (void)test
{
NSLog(@"3");
}
複製程式碼
這段程式碼的執行結果會是什麼呢? 是列印“1、2”,還是“1、3、2”,或者是“1、2、3”?
內容
1.問題探究
這其實是一道很有意思的面試題,內容涉及runloop這個知識點。 答案是隻列印:“1、2”。 原因群裡的大神給瞭解答:
因為
[self performSelector:@selector(test) withObject:nil afterDelay:.0]
實際在runloop裡面,是一個定時器,但是因為在子執行緒,runloop是預設沒有開啟的。
這除了涉及runloop,還有多執行緒的問題,有興趣的可以深究。 其實我們只要仔細閱讀蘋果API的註釋,就能解釋這個問題:
想要執行-test
方法,註釋裡也提供瞭解決辦法:
[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
複製程式碼
其實針對上述的邏輯,更簡單的是:
[self performSelector:@selector(test) withObject:nil];
複製程式碼
2.引發的思考
2.1.不要懶
之所以要提上述的問題,除了這個面試的“考點”,其實在平時的開發過程中也要注意自己程式碼的嚴謹性。
我發現自己在閱讀別人的程式碼時,就見過同樣的寫法,其實甚至那些比較有名的三方庫,例如“YYText
”中,也有類似的程式碼存在:
[self performSelector:@selector(test) withObject:nil afterDelay:0];
複製程式碼
寫這段程式碼的人只是為了通過selector來立刻執行某一方法,delay
並不是他們的需求,為什麼還要“多此一舉”呢?
這裡一大部分原因,很可能還是因為我們被xcode的自動提示給“慣壞了”:
畢竟當你寫程式碼時,羅列的一堆提示,只是按照API相似度排列出來的,很多人看到了自己需要的就直接回車了,不需要delay
,直接寫0,就行了,反正“都一樣”……
其實這是一個誤區,看起來很相似的API,實則並不一樣,而且很不一樣:
-
我們常用的這個perform,是
NSObject.h
這個標頭檔案下的方法: -
可以delay的,是
NSRunLoop.h
下的方法: -
而之前提到的回撥主執行緒的,是
NSThread.h
裡的方法:
雖然他們都是NSObject的方法或者是分類補充方法,但實際上,是隸屬於不同的模組的。
2.2.更深刻的原因
但是“YYText
”的作者應該是不會犯這種低階錯誤的,那就應該還有更深刻的原因了:
我們很多人應該總是會被上述的警告所困擾,大多數人的解決方式,就是利用類似相面的方式去遮蔽警告,這種做法雖然簡單,但實際是有風險的:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//code
#pragma clang diagnostic pop
複製程式碼
其實除了利用IMP
或者NSInvocation
那種比較“高階”的方式,更多的情況下,在方法沒有返回值時,或者我們不需要返回值時,我們可以用:
[self performSelector:@selector(test) withObject:nil afterDelay:0];
複製程式碼
這種方式去避免警告的,看上面的那三個對比你就會發現,後兩類API,同樣是performSelector
,卻沒有返回值,這其實也是有官方註釋的依據的:
但其實你也要注意到了,官方的建議還是很嚴謹的,是用performSelectorOnMainThread
,而不是delay0的方式,至於原因,我們又回到了文章一開頭的討論了。
總結
通過上面看似無意義的探究,我們還是可以得到很深刻的教訓的:“蘋果霸霸”還是很嚴謹的,多看API的註釋,總是沒錯的。
本文作者: 霖溦
本文連結: kukumalucn.github.io/blog/2018/1…
版權宣告: 本部落格所有文章除特別宣告外,均採用 CC BY-NC-ND 4.0 許可協議。轉載請註明出處!