网络编程

当前位置:永利402游戏网站-永利402com官方网站 > 网络编程 > 从计时器到RunLoop

从计时器到RunLoop

来源:http://www.xtcsyb.com 作者:永利402游戏网站-永利402com官方网站 时间:2019-09-16 12:04

基于以前讲的runloop中的CFRunLoopTimerRef :

ios 常用的定时器有三种:NSTime,CADisplayLink和GCD。

  • CFRunLoopTimerRef是基于时间的触发器
  • CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode的影响(Tracking,Defalult)
  • GCD的定时器不受RunLoop中Mode的影响(RunLoop内部也是基于GCD实现的,可以根据源码看到), 比如滚动TableView的时候,GCD的定时器不受影响
  • 一般NSTimer不是特别准, NSTimer是在RunLoop中, RunLoop要处理各种东西(source,timer,observe),有时导致NSTimer不是特别准

NsTimer


GCD定时器算是一个源,类型是Timer

// 参数:Interval:时间间隔  userInfo:携带信息,用来传值    repeats:是否循环

//第一种,使用 timerWithTimeInterval:target:selector:userInfo:repeats: 方法

NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(doAnything) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

//第二种,使用 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomething:) userInfo:@5 repeats:NO];

- (void)doSomething:(NSTimer *)timer{

NSString * str = [timer userInfo];

NSLog(@"%@",str);

}

- (void)doAnything{

NSLog(@"doAnything...");

}

//用第一种方式创建,必须手动将timer加入RunLoop,否则timer的事件是不会响应的,其中 NSDefaultRunLoopMode 代表RunLoop的模式;iOS对外开放的RunLoop Mode有两个;

//  FOUNDATION_EXPORT NSRunLoopMode const NSDefaultRunLoopMode;默认的运行模式,用于大部分操作

// FOUNDATION_EXPORT NSRunLoopMode const NSRunLoopCommonModes NS_AVAILABLE(10_5, 2_0);//是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式。用这种模式,即可以处理页面类似于tableview和scrollerview的滑动事件 ,也可以同时保证定时器的运行

用第二种方法则默认加入RunLoop,且默认模式为NSDefaultRunLoopMode

#import "ViewController.h"@interface ViewController ()@property (nonatomic, strong) dispatch_source_t timer;@end@implementation ViewController- viewDidLoad { [super viewDidLoad]; // 一次性的定时任务// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// NSLog(@"2.0s后执行");// });}int count = 0;- touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // GCD定时器算是一个源 /** * 1. 创建定时器, 创建的定时器为OC对象(注意,这是一个小坑,通过 打印timer:OS_dispatch_source: 0x7f8a81427180, 得出为OC对象,出了作用域就死了 dispatch_source_t 本质为 OC对象, 所以要强引用保证定时器不死 * * @param DISPATCH_SOURCE_TYPE_TIMER 源的类型Timer * @param handle#> 句柄 NULL * @param mask#> NULL * @param queue#> 队列 这里选择的全局队列 * * @return 定时器 这里的定时器不用手动释放,系统取消手动释放了,系统默认释放 */ self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, NULL, NULL, dispatch_get_global_queue; NSLog(@"%@", self.timer); /** * 2. 设置定时器的属性(什么时候开始,多长时间执行一次) * * @param dispatch_source_t source 定时器 * @param dispatch_time_t start 开始时间 * @param uint64_t interval 间隔  1秒 = 10的9次方 纳秒 * @param uint64_t leeway 不需要,写个0 * * @return 定时器 */ dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);//开始时间:从现在开始3秒后开始// dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC), 0); dispatch_source_set_timer(self.timer, start, (2.0 * NSEC_PER_SEC), 0); /** * 3. 设置定时器的回调 */ dispatch_source_set_event_handler(self.timer, ^{ NSLog(@"CGD定时器-----%@",[NSThread currentThread]); count++; if (count == 4) { // 执行4次,让定时器取消 dispatch_cancel(self.timer); self.timer = nil; // 置空,因为是强引用 } }); /** * 4.启动定时器: GCD定时器默认是暂停的 */ dispatch_resume(self.timer); }

// 取消、停止、开始 定时器

//关闭定时器

[myTimer setFireDate:[NSDate distantFuture]];

//开启定时器

[myTimer setFireDate:[NSDate distantPast]];

//取消定时器(用来取消循环执行的定时器,否则可以省略。取消之后要释放。将计数器的repeats设置为YES的时候,self的引用计数会加1。)

[timer invalidate];

timer = nil;

CADisplayLink


CADisplayLink是一个和屏幕刷新率同步的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次,所以可以使用CADisplayLink做一些和屏幕操作相关的操作。相比于NSTimer,CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。但是因为iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。NSTimer则会因为现成阻塞,产生误差

// 创建displayLink

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(change)];

// 将创建的displaylink添加到runloop中,否则定时器不会执行

[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

// 停止定时器

[displayLink invalidate];

displayLink = nil;

- (void)change {

_imageView.transform = CGAffineTransformRotate(_imageView.transform, M_PI / 100);

}

GCD定时器(精度很高,不受RunLoop的Mode影响)


一次性定时

dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);

dispatch_after(timer, dispatch_get_main_queue(), ^(void){

NSLog(@"GCD-----%@",[NSThread currentThread]);

});

循环定时器

@property (nonatomic ,strong)dispatch_source_t timer;//  注意:此处应该使用强引用 strong

{

NSTimeInterval period = 1.0; //设置时间间隔

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行

//要调用的任务

dispatch_source_set_event_handler(_timer, ^{ //在这里执行事件

[self  doAnything];

});

//.开始执行

dispatch_resume(_timer);

//释放

self.timer = nil;

}

GCD定时器时间非常精准,最小的定时时间可以达到1纳秒,所以用在非常精确的定时场合。

RunLoop


RunLoop是线程相关的的基础框架的一部分。一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。其实内部就是do-while循环,这个循环内部不断地处理各种任务(比 如Source,Timer,Observer)。使用run loop的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。

RunLoop是基于线程而存在的,每条线程都有唯一的一个与之对应的RunLoop对象,主线程的RunLoop已经自动创建和启动,子线程的RunLoop需要主动创建、调用run方法启动。RunLoop在第一次获取时创建,在线程结束时销毁

Core Foundation中包含了关于RunLoop的5个类:

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef的接口进行了封装,代表RunLoop的运行模式

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。

系统默认注册了5个Mode:

NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行

UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

CFRunLoopModeRef 类并没有对外暴露,苹果对外暴露的Mode也只有两个;NSDefaultRunLoopMode和NSRunLoopCommonModes,NSRunLoopCommonModes更像是一个模式集合。一个 Mode 可以将自己标记为"Common"属性(通过将其 ModeName 添加到 RunLoop 的 "commonModes" 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 标记的所有Mode里。


假如存在这样一个页面,tableview的headerView是一个轮播图,当tableview被拖动时,轮播图自动播放图片的定时器失效。

通常添加定时器到RunLoop里面,如果选择的Mode为NSDefaultRunLoopMode,那么当tableview拖动时,便会邮NSDefaultRunLoopMode 切换为UITrackingRunLoopMode,如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入,所以在滑动的时候定时器失效;这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响,所以如果把在[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];的Mode换成NSRunLoopCommonModes,在定时器滑动时就不会对定时器产生影响;NSRunLoopCommonModes更像是是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式。(每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 标记的所有Mode里)

CFRunLoopSourceRef

CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。

Source0 非基于Port的

,只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。

基于Port的,通过内核和其他线程通信,接收、分发系统事件

CFRunLoopTimerRef

CFRunLoopTimerRef是基于时间的触发器,CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响,GCD的定时器不受RunLoop的Mode影响

CFRunLoopObserverRef

CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

runloop应用场景除了定时器,还有AutoreleasePool、PerformSelecter,手势识别,界面更行,事件处理等等;

本文由永利402游戏网站-永利402com官方网站发布于网络编程,转载请注明出处:从计时器到RunLoop

关键词:

上一篇:view适应键盘弹出收回的万丈

下一篇:没有了