Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b5ede8b

Browse files
committed
add post
1 parent 88b3af1 commit b5ede8b

File tree

3 files changed

+151
-76
lines changed

3 files changed

+151
-76
lines changed

_config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ jianshu_username: e71990ada2fd
3030

3131
# Build settings
3232
# from 2016, 'pygments' is unsupported on GitHub Pages. Use 'rouge' for highlighting instead.
33-
highlighter: rouge
3433
permalink: pretty
3534
paginate: 10
3635
exclude: ["less","node_modules","Gruntfile.js","package.json","README.md"]
@@ -49,6 +48,7 @@ gems: [jekyll-paginate]
4948
# although redcarpet can auto highlight code, the lack of header-id make the catalog impossible, so I switch to kramdown
5049
# document: http://jekyllrb.com/docs/configuration/#kramdown
5150
markdown: kramdown
51+
highlighter: rouge
5252
kramdown:
5353
input: GFM # use Github Flavored Markdown !important
5454

_posts/2016-12-13-定时器你真的会使用吗?.md

Lines changed: 54 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,22 @@ tags:
1212
---
1313

1414

15-
# iOS定时器
16-
1715
# 前言
1816

19-
定时器的使用是软件开发基础技能,用于延时执行或重复执行某些方法。大部分人接触iOS的定时器都是从
17+
定时器的使用是软件开发基础技能,用于延时执行或重复执行某些方法。
18+
19+
我相信大部分人接触iOS的定时器都是从这段代码开始的:
2020

2121
```objc
2222
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES]
2323
```
2424
25-
这段代码开始的吧。
26-
27-
但是关于iOS定时器,你真的会用吗?
25+
但是你真的会用吗?
2826
2927
# 正文
3028
29+
## iOS定时器
30+
3131
首先来介绍iOS中的定时器
3232
3333
iOS中的定时器大致分为这几类:
@@ -39,6 +39,7 @@ iOS中的定时器大致分为这几类:
3939
### NSTimer
4040
4141
#### 使用方法
42+
4243
**NSTime**定时器是我们比较常使用的定时器,比较常使用的方法有两种:
4344
4445
```objc
@@ -48,7 +49,7 @@ iOS中的定时器大致分为这几类:
4849
```
4950
这两种方法都是创建一个定时器,区别是用`timerWithTimeInterval:`方法创建的定时器需要手动加入RunLoop中。
5051

51-
```objc
52+
```
5253
// 创建NSTimer对象
5354
NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
5455
// 加入RunLoop中
@@ -59,7 +60,7 @@ NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector
5960

6061
举个例子:
6162

62-
```objc
63+
```
6364
- (void)startTimer{
6465
NSTimer *UIScrollView = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(action:) userInfo:nil repeats:YES];
6566
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
@@ -76,7 +77,7 @@ NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector
7677

7778
打印台输出:
7879

79-
![](http://ww1.sinaimg.cn/large/006tNc79gw1farbzzwcevj30ci04ljtm.jpg)
80+
![](http://upload-images.jianshu.io/upload_images/2178672-9de097ecc618b498.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
8081

8182
可以看出在滑动`UIScrollView`时,定时器被暂停了。
8283

@@ -85,22 +86,22 @@ NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector
8586

8687
1. **timer**分别添加到 `UITrackingRunLoopMode``NSDefaultRunLoopMode`
8788

88-
```objc
89-
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
90-
[[NSRunLoop mainRunLoop] addTimer:timer forMode: UITrackingRunLoopMode];
91-
```
89+
```objc
90+
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
91+
[[NSRunLoop mainRunLoop] addTimer:timer forMode: UITrackingRunLoopMode];
92+
```
9293
9394
2. 直接将**timer**添加到`NSRunLoopCommonModes` 中:
9495
95-
```objc
96-
[[NSRunLoop mainRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];
97-
```
96+
```objc
97+
[[NSRunLoop mainRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];
98+
```
9899

99100
但并不是都**timer**所有的需要在滑动`UIScrollView`时继续执行,比如使用**NSTimer**完成的帧动画,滑动`UIScrollView`时就可以停止帧动画,保证滑动的流程性。
100101

101-
若没有特殊要求的话,一般使用第二种方法创建完 **timer**,会自动添加到`NSDefaultRunLoopMode` 中去执行,也是平时最常用的方法。
102+
若没有特殊要求的话,一般使用第二种方法创建完**timer**,会自动添加到`NSDefaultRunLoopMode`中去执行,也是平时最常用的方法。
102103

103-
```objc
104+
```
104105
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES];
105106
```
106107
参数:
@@ -121,7 +122,7 @@ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selecto
121122

122123
释放方法:
123124

124-
```objc
125+
```
125126
// 停止定时器
126127
[timer invalidate];
127128
```
@@ -130,13 +131,13 @@ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selecto
130131

131132
**iOS10.0** 推出了两个新的API,与上面的方法相比,`selector`换成Block回调以、减少传入的参数(那几个参数真是鸡肋)。不过开发中一般需要适配低版本,还是尽量使用上面的方法吧。
132133

133-
```objc
134+
```
134135
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
135136
136137
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
137138
```
138139

139-
#### 特点
140+
###特点
140141

141142
- **必须加入Runloop**
142143

@@ -150,23 +151,23 @@ NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selecto
150151
- **UIScrollView滑动会暂停计时**
151152

152153
添加到`NSDefaultRunLoopMode`的 `timer` 在 `UIScrollView`滑动时会暂停,若不想被`UIScrollView`滑动影响,需要将 `timer` 添加再到 `UITrackingRunLoopMode` 或 直接添加到`NSRunLoopCommonModes` 中
153-
154154

155-
156-
CADisplayLink
157-
---
155+
156+
157+
##CADisplayLink
158+
158159

159160
CADisplayLink官方介绍:
160161
>A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display
161162
162163
**CADisplayLink**对象是一个和屏幕刷新率同步的定时器对象。每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的`target`发送一次指定的`selector`消息, CADisplayLink类对应的 `selector` 就会被调用一次。
163164

164165
从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染,或者做动画。
165-
#### 使用方法
166+
###使用方法
166167

167168
创建:
168169

169-
```objc
170+
```
170171
@property (nonatomic, strong) CADisplayLink *displayLink;
171172
172173
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
@@ -178,7 +179,7 @@ self.displayLink.frameInterval = 1;
178179
```
179180
释放方法:
180181

181-
```objc
182+
```
182183
[self.displayLink invalidate];
183184
184185
self.displayLink = nil;
@@ -196,7 +197,7 @@ self.displayLink = nil;
196197
`CFTimeInterval`值为`readOnly`,表示两次屏幕刷新之间的时间间隔。需要注意的是,该属性在`targe`t的`selector`被首次调用以后才会被赋值。`selector`的调用间隔时间计算方式是:**调用间隔时间 = duration × frameInterval**。
197198

198199

199-
#### 特点
200+
###特点
200201

201202
- **刷新频率固定**
202203

@@ -209,56 +210,28 @@ self.displayLink = nil;
209210

210211
CADisplayLink可以确保系统渲染每一帧的时候我们的方法都被调用,从而保证了动画的流畅性。
211212

212-
GCD定时器
213-
---
213+
##GCD定时器
214+
214215
**GCD定时器**和NSTimer是不一样的,NSTimer受RunLoop影响,但是GCD的定时器不受影响,因为通过源码可知RunLoop也是基于GCD的实现的,所以GCD定时器有非常高的精度。关于GCD的使用可一看看[这篇博客](http://www.cnblogs.com/pure/archive/2013/03/31/2977420.html)
215216

216-
#### 使用方法
217+
###使用方法
217218
创建GCD定时器定时器的方法稍微比较复杂,看下面的代码:
218219

219-
```objc
220-
// 定时时间
221-
int interval = 1;
222-
223-
// 创建全局队列
224-
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
225-
226-
// 创建定时器
227-
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
228-
229-
// 设置定时器 interval * NSEC_PER_SEC 定时间隔几秒
230-
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval * NSEC_PER_SEC, 0);
231-
232-
dispatch_source_set_event_handler(timer, ^{
233-
234-
dispatch_async(dispatch_get_main_queue(), ^{
235-
236-
// 需要执行的代码
237-
});
238-
});
239-
240-
// 开启定时器
241-
dispatch_resume(timer);
242-
243-
// 关闭定时器
244-
dispatch_source_cancel(timer);
245-
```
246-
247-
#### 单次的延时调用
220+
####单次的延时调用
248221
NSObject中的`performSelector:withObject:afterDelay:`以及 `performSelector:withObject:afterDelay:inModes:` 这两个方法在调用的时候会设置当前 runloop 中 `timer` ,前者设置的 `timer``NSDefaultRunLoopMode` 运行,后者则可以指定 **NSRunLoop**`mode` 来执行。我们上面介绍过 runloop 中 `timer``UITrackingRunLoopMode` 被挂起,就导致了代码就会一直等待 `timer` 的调度,解决办法在上面也有说明。
249222

250223
不过我们可以用另一套方案来解决这个问题,就是使用GCD中的 `dispatch_after` 来实现单次的延时调用:
251224

252-
```objc
225+
```
253226
double delayInSeconds = 2.0;
254227
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
255228
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
256229
[self someMethod];
257230
});
258231
```
259232

260-
#### 循环调用
261-
```objc
233+
####循环调用
234+
```
262235
// 创建GCD定时器
263236
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
264237
@@ -300,14 +273,14 @@ dispatch_source_cancel(_timer);
300273
- 创建的`timer`一定要有`dispatch_suspend(_timer)``dispatch_source_cancel(_timer)`这两句话来指定出口,否则定时器将不执行,若我们想无限循环可将 `dispatch_source_cancel(_timer)` 写在一句永不执行的`if`判断语句中。
301274

302275

303-
使用场景
304-
---
276+
##使用场景
277+
305278
介绍完iOS中的各种定时器,接下来我们来说说这几种定时器在开发中的几种用法。
306279
###短信重发倒计时
307280

308281
短信倒计时使我们登录注册常用的功能,一般设置为60s,实现方法如下:
309282

310-
```objc
283+
```
311284
// 计时时间
312285
@property (nonatomic, assign) int timeout;
313286
@@ -377,12 +350,12 @@ dispatch_source_cancel(_timer);
377350

378351
效果如下:
379352

380-
![](http://ww3.sinaimg.cn/large/006tNc79jw1faspxkoemhj30ci0m8mxy.jpg)
353+
![](http://upload-images.jianshu.io/upload_images/2178672-3d4d1353bcc36026.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
381354

382-
[代码的gitHub链接](https://github.com/qiubaiying/BYTimer)
355+
##### [代码链接](https://github.com/qiubaiying/BYTimer)
383356

384357

385-
### 每个几分钟向服务器发送数据
358+
###每个几分钟向服务器发送数据
386359

387360
在有定位服务的APP中,我们需要每个一段时间将定位数据发送到服务器,比如每5s定位一次每隔5分钟将再统一将数据发送服务器,这样会处理比较省电。
388361
一般程序进入后台时,定时器会停止,但是在定位APP中,需要持续进行定位,APP在后台时依旧可以运行,所以在后台定时器也是可以运行的。
@@ -393,7 +366,7 @@ dispatch_source_cancel(_timer);
393366

394367
这里我们使用**NSTimer**来创建一个每个5分钟执行一次的定时器.
395368

396-
```objc
369+
```
397370
#import <Foundation/Foundation.h>
398371
399372
typedef void(^TimerBlock)();
@@ -408,7 +381,7 @@ typedef void(^TimerBlock)();
408381
409382
```
410383

411-
```objc
384+
```
412385
#import "BYTimer.h"
413386
414387
@interface BYTimer ()
@@ -440,12 +413,18 @@ typedef void(^TimerBlock)();
440413
}
441414
442415
@end
443-
444416
```
445417

446-
该接口的实现很简单,就是**NSTimer**创建了一个300s执行一次的定时器,但是要注意定时器需要加入`NSRunLoopCommonModes`中。
418+
该接口的实现很简单,就是 **NSTimer** 创建了一个300s执行一次的定时器,但是要注意定时器需要加入`NSRunLoopCommonModes`中。
419+
420+
要使定时器在后台能运行,app 就需要在 [后台常驻](http://waitingyuan.blog.163.com/blog/static/2155781652014111133150534/)
421+
422+
# 结语
447423

448-
最后,要使定时器在后台能运行,app就需要在[后台常驻](http://waitingyuan.blog.163.com/blog/static/2155781652014111133150534/)
424+
最后总结一下:
449425

426+
NSTimer 使用简单方便,但是应用条件有限。
450427

428+
CADisplayLink 刷新频率与屏幕帧数相同,用于绘制动画。具体使用可看我封装好的一个 [水波纹动画](https://github.com/qiubaiying/WaterRippleView)
451429

430+
GCD定时器 精度高,可控性强,使用稍复杂。

0 commit comments

Comments
 (0)