前言
最近工作比較忙,但是還是出來更新博客了。今天博客中所涉及的內容并不復雜,都是一些平時常見的一些問題,通過這篇博客算是對uitableview中使用定時器的幾種方式進行總結。本篇博客會給出在tableview中使用nstimer或者dispatchsourcer中常見的五種方式。當然下方第一種方式是常規做法,不過也是uitableview中使用nstimer的一個坑。其他三種方式是為了繞過這個坑的解決方案。
當然,本篇博客共涉及到了uitableview中使用定時器的四種實現方式,當然應該也還有其他實現方式,只不過目前我沒有涉及到。歡迎在評論區提供其他實現方式,我會及時的整合到目前的demo中。
接下來我們先來總結一下本篇博客所涉及的四種方式:
- 第一種就是直接在tableview的cell上使用nstimer,當然這種方式是有問題的,稍后會介紹。
- 第二種是將nstimer添加到當前線程所對應的runloop中的commonmodes中。
- 第三種是通過dispatch中的timersource來實現定時器。
- 第四種是開啟一個新的子線程,將nstimer添加到這個子線程中的runloop中,并使用defaultrunloopmodes來執行。
- 第五種方式就是使用cadisplaylink來實現。
下方我們將會根據具體的示例來詳細的介紹以上這五種實現方式。
一、在cell中直接使用nstimer
首先我們按照常規做法,直接在uitableview的cell上添加相應的nstimer, 并使用scheduledtimer執行相應的代碼塊。這種方式沒有什么特殊的就是對timer的直接使用。下方是我們本部分的timer的使用代碼,當然是使用swift來實現的,不過與oc的代碼差不多。代碼如下所示 :
上述代碼比較簡單,就是在cell上添加了一個定時器,然后沒1秒更新一次時間,并在cell的timelabel上顯示,運行效果如下所示。從該運行效果中我們不難發現,當我們滑動tableview時,該定時器就停止了工作。具體原因就是當前線程的runloop在tableview滑動時將defaultmode切換到了trackingrunloopmode。因為timer默認是添加在runloop上的defaultmode上的,當mode切換后timer就停止了運行。
但是當停止滑動后,mode又切換了回來,所以timer有可以正常工作了。
為了進一步看一下mode的切換,我們可以在相應的地方獲取當前線程的runloop并且打印對應的mode。下方代碼就是在tableview所對應的vc上添加的,我們在viewdidload()、viewdidappear()以及scrollviewdidscroll()這個代理方法中對當前線程所對應的runloop下的currentmode進行了打印,其代碼如下。
下方就是最終的運行結果。從輸出結果中我們不難看出,在viewdidload()方法中打印的current mode為uiinitializationrunloopmode, 從該mode的名字中我們不難發現,該mode負責ui的初始化。在viewdidapperar()方法中,也就是ui顯示后,runloop的mode切換成了kcfrunloopdefaultmode。緊接著,我們去滑動tableview,然后在scrollviewdidscroll()代理方法中打印滑動時當前runloop所對應的mode。從下方運行結果不難看出,當tableview滑動時,打印出的currentmodel為uitrackingrunloopmode。當停止滑動后,點擊show current mode按鈕獲取當前mode時,打印的有時runloopdefaultmode。具體如下所示:
二、將timer添加到commonmode中
上一部分的定時器是不能正常運行的,因為nstimer對象默認添加到了當前runloop的defaultmode中,而在切換成trackingrunloopmode時,定時器就停止了工作。解決該問題最直接方法是,將nstimer在trackingrunloopmode中也添加一份。這樣的話無論是在defaultmode還是trackingrunloopmode中,定時器都會正常的工作。
如果你對runloop比較熟悉的話,可以知道commonmodes就是defaultmode和trackingrunloopmode的集合,所以我們只需要將nstimer對象與當前線程所對應的runloop中的commonmodes關聯即可,具體代碼如下所示:
上述代碼與第一部分的代碼不同的地方在于我們將創建好的定時器添加到了當前runloop中的commonmodes中,這樣的話可以保證tableview在滑動時定時器也可以正常運行。上述代碼最終的運行效果如下所示。
從該運行效果我們不難發現,當該tableview滾動式,其cell上的定時器是可以正常工作的。但是當我們滑動右上角的這個tableview時,第一個的tableview中的定時器也是不能正常工作的,因為這些tableview都在主線程中工作,也就是說這些tableview所在的runloop是同一個。
三、將timer添加到子線程的runloop下的defaultmode中
接下來我們來看另一種解決方案,就是開啟一個新的子線程,然后將timer添加到這個子線程所對應的runloop中。當然因為是子線程的runloop,在添加timer時,我們可以將timer添加到子線程中的runloop中的defaultmode中。添加完畢后,手動運行該runloop。
因為是在子線程中添加的timer, timer肯定是在子線程中工作的,所以在更新ui時,我們需要在主線程中進行更新,具體代碼如下所示:
在上述代碼中我們可以看到我們使用全局的并行隊列來異步創建了一個timer對象,然后將該對象添加進了該異步線程中的defaultrunloopmode中,然后運行該runloop。當然在子線程中更新ui還是需要在主線程中去操作的。下方就是上述代碼的運行效果。從該效果中我們不難看出,當滑動tableview時定時器是可以正常工作的。
四、dispatchtimersource
接下來我們就不使用nstimer來實現定時器了。在之前的博客中聊gcd時其中用到了dispatchtimersource來實現定時器。接下來我們就在tableview的cell上添加dispatchtimersource,然后看一下運行效果。當然下方代碼片段我們是在全局隊列中添加的dispatchtimersource,在主線程中進行更新。當然我們也可以在mainqueue中添加dispatchtimersource,這樣也是可以正常工作的。當然我們不建議在mainqueue中做,因為在編程時盡量的把一些和主線程關聯不太大的操作放到子線程中去做。代碼如下所示:
接下來我們來看一下上述的代碼的運行效果,從該效果中我們可以看出該定時器是可以正常工作的。
五、cadisplaylink
接下來我們來使用cadisplaylink來實現定時器功能,在之前的博客中我們也使用過cadisplaylink,不過是用來計算fps的。下方代碼片段中我們就使用cadisplaylink來實現的定時器。cadisplaylink可以添加到runloop中,runloop的每一次循環都會觸發cadisplaylink所關聯的方法。在屏幕不卡頓的情況下,每次循環的時間時1/60秒。
下方代碼,為了不讓屏幕的卡頓等引起的主線程所對應的runloop阻塞所造成的定時器不精確的問題。我們開啟了一個新的線程,并且將cadisplaylink對象添加到這個子線程的runloop中,然后在主線程中更新ui即可。具體代碼如下:
我們對上述代碼運行,下方是其對應的運行結果。從下方運行結果中我們不難看出,在tableview滾動時該定時器也是可以正常運行的。當然該方式實現的定時器的精度是比較高的。
經過上述五大部分,我們羅列了定時器的幾種實現方式,通過對比我們不難發現其優劣性。上述定時器中dispatchsourcetime以及cadisplaylink的精度要比nstimer的精度要高。從代碼實現中我們不難看出cadisplaylink的精度是比較高的。
本篇博客所涉及代碼的github分享地址為:https://github.com/lizelu/nstimerwithrunloop
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/ludashi/p/7349535.html