从定时器到RunLoop

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

NsTimer


// 参数: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

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

//关闭定时器

[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,手势识别,界面更行,事件处理等等;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容