工作需要,粗略了解了ios上的幾種定時(shí)器。
NSTimer:
? ? ? ? ? ?這個(gè)底層怎么定時(shí)我不清楚,應(yīng)該是到了一定時(shí)間之后,向runLoop添加一個(gè)事件,這也就意味著,如果想在異步線程開啟timer ,就需要手動(dòng)的將timer 添加到runLoop中,并且run起來。
? ? ? ? ? ?網(wǎng)上看了一些說的精確度,不是明白,親測(cè)過,子線程創(chuàng)建一個(gè)timer,runLoop run起來,只要timer里面的事件,不超過timer定時(shí)的時(shí)間,timer定時(shí)還是很準(zhǔn)確的,所以如果項(xiàng)目需要,timer里面的執(zhí)行事件的時(shí)間超過了定時(shí)器的時(shí)間,就會(huì)出問題了(試想一下,即使把事件再拿到另外一個(gè)線程去執(zhí)行,執(zhí)行時(shí)間永遠(yuǎn)都是大于定時(shí)時(shí)間的,即使不影響定時(shí)時(shí)間,那么事件處理的堆積事件就會(huì)越來越多,肯定無(wú)法滿足需求,所以最好的是,將事件拆分,做成多個(gè)管道,多個(gè)定時(shí)器,定時(shí)器時(shí)間大于處理定時(shí)器事件的時(shí)間)。我們通常說的timer會(huì)出問題,我猜想可能是在主線程定義了一個(gè)timer,runloop執(zhí)行一個(gè)耗時(shí)很長(zhǎng)的循環(huán),錯(cuò)過了timer的周期,這肯定會(huì)出問題,還有就是timer里面的事件處理時(shí)間不夠也會(huì)出問題。所以我的解決辦法是,子線程runLoop run起來,保持線程,同時(shí)保證timer里面的事件處理時(shí)間不超過timer時(shí)間,這樣就ok了。
? ? ? ? ? 綜上所訴,用處理一般的事件完全足夠,只是使用中需要注意。
CADisplayLink:
? ? ? ? ? 這個(gè)是根據(jù)屏幕的幀率來發(fā)送事件,也依賴于runLoop,也就是說,我們只能控制多少幀,觸發(fā)我們定義的事件,可用于界面的刷新,不能隨心所欲的控制時(shí)間,不滿足我們一般的需求。使用也很簡(jiǎn)單,自行看API
GCD的source timer:
? ? ? 個(gè)人覺得這是比較給力(原諒我詞匯的匱乏)的一個(gè)定時(shí)器了。原因如下,它不依賴于runLoop,底層兩個(gè)隊(duì)列,一個(gè)事件隊(duì)列,一個(gè)任務(wù)隊(duì)列,定時(shí)到了之后,從任務(wù)隊(duì)列里取出事件,加入到事件隊(duì)列(之前還有一步,向任務(wù)隊(duì)列push任務(wù)),執(zhí)行事件,至于他的一直執(zhí)行(為什么不依賴于runLoop,這于GCD的底層實(shí)現(xiàn)有關(guān),有興趣可以自行了解),放到一個(gè)子線程執(zhí)行(想在主線程執(zhí)行的,可以用mainQUeue包一層),在子線程的執(zhí)行順序也是串行的,所以如果block的執(zhí)行事件時(shí)間大于定時(shí)時(shí)間,還是會(huì)阻塞,可以一定層度上解決,請(qǐng)看下面代
可以在block塊里再用一個(gè)隊(duì)列,異步執(zhí)行,包起來,但是我們看一下執(zhí)行結(jié)果
確實(shí),執(zhí)行事件表面看并沒有影響定時(shí)器的定時(shí),但是注意看始終都是系統(tǒng)分配的幾個(gè)線程去執(zhí)行事件,也就是說這幾個(gè)線程是重復(fù)利用的,當(dāng)?shù)谝淮问褂猛辏诙问褂玫臅r(shí)候,定時(shí)并不準(zhǔn)確(可能是線程清理需要事件,具體不清楚),還有定時(shí)器的定時(shí)還是不準(zhǔn)確,這可能就跟GCDSource內(nèi)部實(shí)現(xiàn)有關(guān)系了,所以不推薦這樣使用,目前我個(gè)人的想法就是,如果事件處理時(shí)間過長(zhǎng),要嗎想辦法拆分事件,加管道,加定時(shí)器,要嗎延長(zhǎng)定時(shí)時(shí)間。
與timer相比,的優(yōu)勢(shì)就是,不需要維持runLoop,(性能消耗上或許有區(qū)別,沒有實(shí)際看過),還有本身就是子線程去執(zhí)行的,不會(huì)阻塞主線程。
以上只是我個(gè)人的想法和總結(jié),肯定有說得不對(duì)的地方,希望多討論,多批評(píng)指正。