關於performSelector:afterDelay:的一個坑及思考

原文連接: kukumalucn.github.io/blog/2018/1…git

前言

剛在羣裏看到這樣一段代碼,頗有意思:github

- (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");
}
複製代碼

這段代碼的執行結果會是什麼呢? 是打印「一、2」,仍是「一、三、2」,或者是「一、二、3」?面試

內容

1.問題探究

這實際上是一道頗有意思的面試題,內容涉及runloop這個知識點。 答案是隻打印:「一、2」。 緣由羣裏的大神給瞭解答:xcode

由於[self performSelector:@selector(test) withObject:nil afterDelay:.0]實際在runloop裏面,是一個定時器,可是由於在子線程,runloop是默認沒有開啓的。多線程

這除了涉及runloop,還有多線程的問題,有興趣的能夠深究。 其實咱們只要仔細閱讀蘋果API的註釋,就能解釋這個問題:async

想要執行-test方法,註釋裏也提供瞭解決辦法:oop

[self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
複製代碼

其實針對上述的邏輯,更簡單的是:ui

[self performSelector:@selector(test) withObject:nil];
複製代碼

2.引起的思考

2.1.不要懶

之因此要提上述的問題,除了這個面試的「考點」,其實在平時的開發過程當中也要注意本身代碼的嚴謹性。 我發現本身在閱讀別人的代碼時,就見過一樣的寫法,其實甚至那些比較有名的三方庫,例如「YYText」中,也有相似的代碼存在:spa

[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 許可協議。轉載請註明出處!

相關文章
相關標籤/搜索