iOS开发之UITableView中计时器的几种实现方式(NSTimer、DispatchSource、CADisplayLink)

最近工作比较忙,但是还是出来更新博客了。今天博客中所涉及的内容并不复杂,都是一些平时常见的一些问题,通过这篇博客算是对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 ModeUIInitializationRunLoopMode, 从该Mode的名字中我们不难发现,该Mode负责UI的初始化。在viewDidApperar()方法中,也就是UI显示后,RunLoop的Mode切换成了kCFRunLoopDefaultMode。紧接着,我们去滑动TableView,然后在scrollViewDidScroll()代理方法中打印滑动时当前RunLoop所对应的Mode。从下方运行结果不难看出,当TableView滑动时,打印出的currentModel为UITrackingRunLoopMode。当停止滑动后,点击Show Current Mode按钮获取当前Mode时,打印的有时RunLoopDefaultMode。具体如下所示:

   

 

 

二、将Timer添加到CommonMode中

上一部分的定时器是不能正常运行的,因为NSTimer对象默认添加到了当前RunLoopDefaultMode中,而在切换成TrackingRunLoopMode时,定时器就停止了工作。解决该问题最直接方法是,将NSTimer在TrackingRunLoopMode中也添加一份。这样的话无论是在DefaultMode还是TrackingRunLoopMode中,定时器都会正常的工作。

如果你对RunLoop比较熟悉的话,可以知道CommonModes就是DefaultModeTrackingRunLoopMode的集合,所以我们只需要将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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/399747.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS查看每个进程的网络流量

所需工具nethogs 安装:yum install -y nethogs 使用:nethogs eth0 sudo nethogs -s //按接收流量大小排序 如上图,PID一列就是进程的PID,PROGRAM就是显示进程或连接双方的端口号,前面红框是服务器的IP:端口&#xff0…

转:Java反射教程

原文来自于:http://www.importnew.com/9078.html 什么是反射?反射有什么用处? 1. 什么是反射? “反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。”这个概念常常会和内省(Int…

浅入浅出数据结构(20)——快速排序

正如上一篇博文所说,今天我们来讨论一下所谓的“高级排序”——快速排序。首先声明,快速排序是一个典型而又“简单”的分治的递归算法。 递归的威力我们在介绍插入排序时相比已经见识过了:只要我前面的队伍是有序的,我就可以通过向…

结对第一次作业

同学A : 031502630 - 吴松青 同学B : 031502644 - 邹星 第一次结对作业 本次作业的要求是设计一个方便部门纳新与学生选择部门的app,当然只是原型......刚开始怕要求实现的我们畏首畏尾,总得考虑到后期的实现的困难。最后老师提醒我们不需要实现后&#…

仿美团实现地域选择和城市列表

介绍 在开发O2O相关应用的时候,肯定会有定位,选择所在城市,选择地域,然后再向服务器请求该地区的相关数据,这时就需要我们提供一个导向让用户选择所在区域。 看来看去,最终还是选择模仿美团,感觉…

Ubuntu16.04中php如何切换版本

其实就是一条Linux命令,如下: sudo update-alternatives --config php 会出现下面选项: There are 2 choices for the alternative php (providing /usr/bin/php).Selection Path Priority Status -------------------------------------------------------…

MAC下面maven如何设置让其实下载源码

2019独角兽企业重金招聘Python工程师标准>>> Eclipse--->偏好设置---- >Maven--->download artifact source 转载于:https://my.oschina.net/u/2422498/blog/500292

EventBus使用详解(一)——初步使用EventBus

前言:EventBus是上周项目中用到的,网上的文章大都一样,或者过时,有用的没几篇,经过琢磨,请教他人,也终于弄清楚点眉目,记录下来分享给大家。 相关文章: 1、《EventBus使用…

Android应用程序打包时,出现错误:XXX is not translated in af (Afrikaans), am (Amharic), ar (Arabic).....(...

转自:http://blog.163.com/shexinyang126/blog/static/136739312201492144928812/ 问题:当我们开发完成一个Android应用程序后,在发布该应用程序之前必须要经过的一步时打包应用程序。 至于从打包程序到发布的完整过程可以参考: A…

如何拿到阿里算法校招offer

好多同学有问过怎么能拿到阿里算法类校招的offer,刚好看到这篇文章分享给大家,详情可以看原文链接,原文链接中有视频讲解。 师兄师姐的建议: 之前初学算法的时候上过的公开课和看过的书 1. Coursera:《Machine Learnin…

通用软件/工具手册

为什么80%的码农都做不了架构师?>>> #sublime text ##Settings - User {"font_size": 14.0,"tab_size": 2,"scroll_past_end": true,"translate_tabs_to_spaces": true,"trim_trailing_white_space_on_sa…

优秀的SharePoint 2013开发工具有哪些(二)

SharePoint 2013 Search Tool 搜索功能是SharePoint2013的一大亮点。SharePoint 2013 Search Tool可以让我们学习和了解查询如何被格式化,并让我们轻松地配置一个Search REST Query。使用SharePoint 2013 Search Tool来创建你的查询,就可以对它们进行分…

使用jquery解析xml

使用Jquery解析XML:$.ajax({ url: ajax/test.xml, dataType : xml, cache: false, success: function(xml) { $("AUTHOR", xml).each(function(id) { AUTHOR $("AUTHOR", xml).get(id); …

cv1159 最大全0子矩阵(极大子矩阵)

题目描述 Description 在一个01方阵中找出其中最大的全0子矩阵&#xff0c;所谓最大是指0的个数最多。 输入描述 Input Description 输入文件第一行为整数N&#xff0c;其中1<N<2000&#xff0c;为方阵的大小&#xff0c;紧接着N行每行均有N个0或1&#xff0c;相邻两数…

Docker认识基础

版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/48212081 Docker认识基础 作者&#xff1a;chszs&#xff0c;版权所有&#xff0c;未经同意&#xff0c;不得转载。博主主页&#xff1a;http:…

信管 - 挣值 - 资料收集

信息系统项目管理师计算题之挣值分析、完工预测知识与习题 挣值分析&#xff1a;早期只需要记住三个参数&#xff0c;4个指标以及公式即可。PV、EV、AC、CV、SV、CPI、SPI。但现在没这么简单了&#xff0c;深入考核PV、EV、AC的理解&#xff0c;从一段文字描述中计算出PV、EV、…

hdu5424 Rikka with Graph II

给一个n个节点n条边的无向图G&#xff0c;试判断图中是否存在哈密顿路径。 若G中存在哈密顿路径l&#xff0c;则路径端点度数不小于1&#xff0c;其余点度数不小于2。 则G存在哈密顿路径的必要条件&#xff1a; 1&#xff09;G连通&#xff1b; 2&#xff09;G中度数为1的点不超…

VisualStudio中的代码段

VS很强大&#xff0c;在这里就不过多说了&#xff0c;在平时码代码时应用代码段会提高我们的编写速度。 举个例子&#xff1a; 比如输入Console.WriteLine (); 传统方法就是一个字母一个字母的输入进去。 如果大家掌握了代码段&#xff0c;就变得非常简单了。只需要输入cw按两次…

tcp和udp的区别和三次 四次挥握手 http://www.cnblogs.com/bizhu/archive/2012/05/12/2497493.html...

小结TCP与UDP的区别&#xff1a;1.基于连接与无连接&#xff1b;2.对系统资源的要求&#xff08;TCP较多&#xff0c;UDP少&#xff09;&#xff1b;3.UDP程序结构较简单&#xff1b;4.流模式与数据报模式 &#xff1b;5.TCP保证数据正确性&#xff0c;UDP可能丢包&#xff0c;…

java concurrent包介绍及使用

2019独角兽企业重金招聘Python工程师标准>>> 说一说java的concurrent包1-concurrent包简介 前面一个系列的文章都在围绕hash展开&#xff0c;今天准备先说下concurrent包&#xff0c;这个系列可能会以使用场景说明为主&#xff0c;concurrent包本身的代码分析可能比…