iOS 定时器开发详情
目录
概述
NSTimer
performSelector
GCD timer
CADisplayLink
一、概述
在平时的开发任务中,定时器是我们常用的技术。这一节我们来学习iOS怎么使用定时器。
在iOS中用一个timer对象来表示一个定时器,这个timer对象必须关联到一个runloop对象才能够正常运行。也就是说,runloop对象是timer对象的拥有者,当定时器的时间到期时由runloop对象给timer发通知,所以runloop对象是持有timer对象的强引用的。如果是一次性的定时器话,当定时器到期时,runloop放弃持有timer对象的引用。但如果是循环timer的话 runloop会一直持有timer的引用直到timer调用invalidate。
timer对象关联到runloop对象时需要指定一个runloop mode,默认为default。当定时器到期时,并且runloop运行的mode与timer所关联的mode相同情况下,runloop才会给timer发通知。
假设timer关联到default mode,当runloop运行在 tracking modes时(滑动的时候),即使timer到期了也是不会被触发的。
正是因为timer需要runloop才起作用所以timer是有误差的:当一次runloop循环时检查timer是否满足触发条件,如果不满足则等待下次循环再检查。timer的误差大概是50~100ms,可满足一般对精度要求不高的需求。
在iOS开发中,我们可以四种技术来开发定时器:
- NSTimer - timer对象的cocoa类
- performSelector - NSObject的函数
- GCD timer - dispatch 内的
- CADisplayLink - 与屏幕刷新频率一致的timer
一般来说,如果只是延迟执行的定时器,我们多会用display_after、NSObject的performSelector函数。
一般的循环定时器可以用NSTimer跟 dispatch_source的timer。
CADisplayLink则多用于动画或者视频开发当中
二、NSTimer
创建一个timer的步骤是:
1.创建一个NSTImer
2.把timer添加到runloop
self.unscheduledTimer = [NSTimer timerWithTimeInterval:
target:self
selector:@selector(targetMethod:)
userInfo:nil
repeats:NO]; // 添加到runloop
// NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// [runLoop addTimer:self.unscheduledTimer forMode:NSRunLoopCommonModes];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.unscheduledTimer forMode:NSRunLoopCommonModes];
});
上面代码中,创建一个3s后执行的timer,但是2s后才把这个timer添加到runloop中,3s后就会触发timer(也就是添加到runloop后1s)。
假设我们4s钟后才把timer添加到runloop中,这时的timer会立马被触发。
整过程是这样的,当一个timer被添加到runloop时,runloop会检查这个timer是否到期如果到期了就会立刻触发这个timer,如果没有过期就等待下次runloop时再检查。
我们也可以用下面函数生成一个定时器:
[NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(targetMethod:)
userInfo:nil
repeats:NO];
scheduledTimerWithTimeInterval函数做两件事:
1.生成一个timer并用传入的参数配置它
2.把生成的timer以default mode添加到当前的runloop的。
三、performSelector
performSelector则比较简单了,可以用于一般的延迟任务:
[self performSelector:@selector(targetMethod:) withObject:nil afterDelay:3];
四、GCD timer
GCD的dispath_after函数很好用,可以用于一般的延迟任务:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 三秒钟后执行
});
还有可以利用dispatch_source来创建timer
__block int count = ;
/*
* dispatch_source_t dispatch_source_create( dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
* 第一个参数指明这是一个timer,当然还可以指定其他类型
* 第二个参数是一个系统资源的句柄,比如文件句柄
* 第三个参数为flag
* 第四个参数是handle block被提交到的queue
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , dispatch_get_main_queue());
if (timer) {
// 设置 timer
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, * NSEC_PER_SEC, );
// 设置 event handler
dispatch_source_set_event_handler(timer, ^{
NSLog(@"timer fire");
if (count++ == ) {
// 取消dispatch source
dispatch_source_cancel(timer);
}
});
// 设置 event handler
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"time cancel.");
});
// 启动 dispatch source
// 因为create后还需要配置一些行为,所以需要手动resume dispatch source
dispatch_resume(timer);
// 注意:
// 这里需要保存一下timer到类变量,不然timer是局部变量运行到函数尾部时,这个定时器也就没了
self.timer = timer;
}
需要说明的是,通过dispatch_source_create生成并返回的变量,不能是局部变量。因为如果是局部变量的话,在生命周期结束时这个source就被释放了。这一点与NSTimer不一样,因为当NSTimer被添加到runloop时,runloop就会持有NSTimer的强引用。
所以如果我们需要一个定时器,并想让这个定时器生命周期跟我们的业务类的生命周期一致时,可以用dispatch_source的方式创建定时器,然后把这个定时器保存为我们的业务类的类成员变量。
五、CADispalyLink
CADisplayLink是一个特殊的timer对象,特殊的在于这个timer的触发的频率跟屏幕刷新的频率是一致的。也就是说,每当屏幕刷新一次就会调用一次timer的回调函数,当然我们可以使设置frameInterval属性来指明刷新多少帧后才触发一次timer,frameInterval默认为1.
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(targetMethod:)];
// [self.displayLink setFrameInterval:24] // 24帧回调一次
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
iOS 定时器开发详情的更多相关文章
- ios新手开发——toast提示和旋转图片加载框
不知不觉自学ios已经四个月了,从OC语法到app开发,过程虽然枯燥无味,但是结果还是挺有成就感的,在此分享我的ios开发之路中的小小心得~废话不多说,先上我们今天要实现的效果图: 有过一点做APP经 ...
- Search Ads 已经在美国区上线 - iOS 移动开发周报(46)
Search Ads 已经在美国区上线 - iOS 移动开发周报(46) 新闻 Search Ads 上线 苹果的 AppStore 搜索广告终于 正式上线了 https://developer.ap ...
- ios程序开发杂记
ios程序开发杂记 一.程序构建 与一般的程序构建无太大区别,都是源文件编译链接这一套,通常是在mac上做交叉编译,也就是利用xcode里带的ios编译工具集去生成arm架构的ios程序(或是x86的 ...
- iOS应用开发最佳实践
<iOS应用开发最佳实践> 基本信息 作者: 王浩 出版社:电子工业出版社 ISBN:9787121207679 上架时间:2013-7-22 出版日期:2013 年8月 开本:16 ...
- iOS定时器-- NSTimer 和CADisplaylink
iOS定时器-- NSTimer 和CADisplaylink 一.iOS中有两种不同的定时器: 1. NSTimer(时间间隔可以任意设定,最小0.1ms)// If seconds is les ...
- 聚合数据 iOS 项目开发实战:条码查询器
记录下,聚合数据 iOS 项目开发实战:条码查询器:视频地址:http://www.jikexueyuan.com/course/324.html 条码查询API:https://www.juhe.c ...
- 移动开发在路上-- IOS移动开发系列 多线程二
最近太忙没太多的时间,忙碌的码农生活空下来一点时间,都会挤出来看一些技术或者咨询的文章,废话不多说,直奔主题. 接着上一次的继续说. 定时器在多线程的使用 NSRunLoop 是线程相关的基础框架的一 ...
- 中文 iOS/Mac 开发博客列表
中文 iOS/Mac 开发博客列表 博客地址 RSS地址 OneV's Den http://onevcat.com/atom.xml 一只魔法师的工坊 http://blog.ibireme.com ...
- Unity iOS混合开发界面切换思路
Unity iOS混合开发界面切换思路 最近有很多博友QQ 私信 或则 留言联系我,请教iOS和Unity界面之前相互切换的问题,源代码就不私下发你们了,界面跳转功能的代码我直接贴到下面好了,顺带说i ...
随机推荐
- dcm4che 的依赖无法下载
遇到问题时我在Gradle这样引入 maven { url "http://www.dcm4che.org/maven2"} 这样使用可以解决问题 maven { url &quo ...
- 【例题收藏】◇例题·6◇ 电压机制(voltage)
◆例题·6◆ 电压机制 周六日常模拟赛……已经不知道该说什么了(感觉做不出来的都是好题) ▷ 题目 (终于不用自己翻译英文题了╮(╯-╰)╭) [问题描述] 科学家在“无限神机”(Infinity M ...
- 一个BUG?Visual Studio 2017 C++编写交换两个整数
想用一句话搞定交换: int a = 2, b = 5; cout << "a = " << a << ", b = " & ...
- 爬虫——json模块与jsonpath模块
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写.同时也方便了机器进行解析和生成.适用于进行数据交互的场景,比如网站前台与后 ...
- Ajax异步交互
一.简介 Ajax(Asynchronous JavaScript and XML).一般都写为Ajax. Ajax是与服务器交换数组并更新部分网页的艺术.最初的使用时2005中Google Sugg ...
- 02 shell编程之条件语句
Shell编程之条件语句 学习目标: 掌握shell脚本条件测试 掌握if语句编程 目录结构: 条件测试 条件测试概述 l 对特定的条件进行判断,以决定如何执行操作 l 测试的方法 方法1:tes ...
- iframe中的页面在IE全屏模式下没有滚动条,正常模式有滚动条
这个问题在其他浏览器都不会出现,唯独IE不行,搜遍了百度以及各大论坛网站,都找不到这个问题的解决方案,只好自己整了. 造成这个问题的原因很简单,就是刚开始的滚动条我用的是iframe的滚动条,ifra ...
- PHP实现微信红包算法和微信红包的架构设计简介
微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...
- docker配置与实践#可以好好看看
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源.Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 L ...
- Python学习之模块基础
模块就是程序 编写以下简单代码 print('hello python') 并将py文件保存在c盘的python(假设新建)文件下,通过pycharm的Terminal 或者windom命令窗口调出p ...