前言
说道runloop,想必大家都只是稍微知道这玩意是干嘛的吧,以前也总是看这方面的书籍,并没有做下任何笔记,索性这段时间刚好整理项目,那么就把自己的想法记下来,也许会对大家有一些帮助吧。
其实大家都喜欢把它和线程结合起来说明,确实他们之间的联系千丝万缕,下面就给大家介绍一下runloop这个东东吧
结合实际应用说明,有不好的地方请指教
知识点一:
ios中runloop提供了俩种:CFRunloopRef 和 NSRunloop
CFRunloopRef : 底层的 基于C的框架,所以是线程安全的
NSRunloop:是对CFRunloopRef的封装 是面向对象的 都非线程安全的
知识点二:
runloop使用的场景:
1.大家都知道除了主线程外,都是有生命周期的,一旦线程内的所有任务完成的时候,线程会自动结束掉生命周期,自动释放掉。那么如果想要保持线程的存活,随时等待消息,那么runloop就派上用场了。
2.如果人为地去持有线程,使其不被释放,从而去重复调用[thread start],这时候程序会崩溃,给出一条这样的提示“attempt to start the thread again”。这是为什么呢,因为线程在执行完自己的任务后会进入“假死”状态,进入假死状态的线程呢,并不享有重新开启的功能 所有此时会系统报错。
3.所有这个时候就引入了“runloop”的概念,当一个线程持有了所谓的“runloop”的时候,那么它就能保持常存的状态了。
知识点:
其实引入的runloop相当于do while的循环体,其本质如下
一直处于激活的状态,当接受到消息的时候 就进行执行相应的任务,当完成任务的时候就进入“休眠”,直到下个任务的到来。或者被通知退出。
4.线程创建时并没有自动创建runloop,而是当我们去调用的时候,这时候才会去创建,有点像我们所说的“懒加载”。
5. 主线程例外,当app运行的时候 会默认创建主线程的runloop。主线程能自动切换model。
6.runloop运行的几大条件:
1. model
mode litemps:RunLoop同一时间只能运行在一种Mode下,当前运行的这个Mode叫currentMode。
model的三大类型:
1.kCFRunLoopDefaultMode(CFRunLoop)/NSDefaultRunLoopMode(NSRunLoop)默认模式,在RunLoop没有指定Mode的时候,默认就跑在DefaultMode下。一般情况下App都是运行在这个mode下的
2(CFStringRef)UITrackingRunLoopMode(CFRunLoop)/UITrackingRunLoopMode(NSRunLoop)一般作用于ScrollView滚动的时候的模式,保证滑动的时候不受其他事件影响。
3.kCFRunLoopCommonModes(CFRunLoop)/NSRunLoopCommonModes(NSRunLoop)这个并不是某种具体的Mode,而是一种模式组合,在主线程中默认包含了NSDefaultRunLoopMode和UITrackingRunLoopMode。子线程中只包含NSDefaultRunLoopMode。注意:①在选择RunLoop的runMode时不可以填这种模式否则会导致RunLoop运行不成功。②在添加事件源的时候填写这个模式就相当于向组合中所有包含的Mode中注册了这个事件源。③你也可以通过调用CFRunLoopAddCommonMode()方法将自定义Mode放到 kCFRunLoopCommonModes组合。
Model Item:
source就是输入源事件,分为source0和source1这两种。
Timer即为定时源事件
Observer它相当于消息循环中的一个监听器
总结:
RunLoop能正常运行的条件就是,至少要包含一个Mode(RunLoop默认就包含DefaultMode),并且该Mode下需要有至少一个的事件源(Timer/Source),另外特别注意的一点是:事件源Sources必须在对应的Model下,不然也是没有作用的,好比如下:
[runLoop addPort:[NSMachPortport] forMode:UITrackingRunLoopMode];
模式选择错误 此处的model应该选成defalut。
启动runloop的三种方法:
1.- (void)run;
正常启动,会一直存在,除非希望子线程永远存在,否则不建议使用,因为这个接口会导致Run Loop永久性的运行NSDefaultRunLoopMode模式,即使使用 CFRunLoopStop(runloopRef);也无法停止RunLoop的运行,那么这个子线程也就无法停止,只能永久运行下去。
2.- (void)runUntilDate:(NSDate*)limitDate;
一般结合while循环使用
while(!Stop){
[[NSRunLoopcurrentRunLoop] runUntilDate:[NSDatedateWithTimeIntervalSinceNow:10]];
}
3.- (BOOL)runMode:(NSString*)mode beforeDate:(NSDate*)limitDate;
有一个超时时间限制,而且可以设置运行模式。这个接口在非Timer事件触发、显式的用CFRunLoopStop停止RunLoop或者到达limitDate后会退出返回。如果仅是Timer事件触发并不会让RunLoop退出返回,但是如果是PerfromSelector事件或者其他Input Source事件触发处理后,RunLoop会退出返回YES。同样可以像上面那样用while包起来使用。
总结:
1.runloop是于线程伴生的,与线程是一一对应的
2.runloop不能手动创建 只能获取当前的
3.子线程中,runloop需要我们手动去开启,但是主线程不需要
4.runloop的运行条件:model;model item;并且 要相对应。
下面介绍下如何让指定的线程接受我们的消息(注意,是开启了runloop的线程)
下面是demo:
注意:这里的指向必须用weak属性修饰,不然被一直被引用 无法释放。
NStimer
有人好奇,为什么要把NSTimer放到runloop的篇章中来讲。
知识点:
1.Nstimer其实是需要放到Runloop中去实现,否则无法正常使用,
例如:
NSTimer*timer = [NSTimertimerWithTimeInterval:1target:selfselector:@selector(wantTodo) userInfo:nilrepeats:YES];//timerWith开头的方法创建的Timer如果不加下面一句无法运行。
[[NSRunLoopcurrentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
其实另外一种类方法可以不需要手动将timer注册到runloop中:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];
这是因为其类方法中自动将其注册到runloop中了,无需手动添加。
思考一个问题:为什么屏幕滑动阶段,NStimer会失去作用 ,当停止滑动的时候却又失效?
解答:
首先慢慢来思考一下,NStimer是注册到主线程中的runloop的NSDeflautModel模式中,本着runloop只能每次运行一种model,而当屏幕滑动的时候,主线程自动切换到UITrackingRunLoopMode模式下的事件源中,这时候NStimer自然就停止了,而当滑动结束后,又切换回NSDeflautModel模式下,这时候nstimer又能正常使用了;
那么如何能让这俩者并存呢,其实不难,
[[NSRunLoopcurrentRunLoop] addTimer:timer forMode:NSCommonRunLoopMode];就可以了;
或者
[[NSRunLoopcurrentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoopcurrentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; 同时添加.
.
以上就是个人总结 有些的不好的地方 请多多指教
网友评论