美文网首页程序员
浅谈IOS的Runloop

浅谈IOS的Runloop

作者: Mr姜饼 | 来源:发表于2018-11-27 15:36 被阅读0次

前言

说道runloop,想必大家都只是稍微知道这玩意是干嘛的吧,以前也总是看这方面的书籍,并没有做下任何笔记,索性这段时间刚好整理项目,那么就把自己的想法记下来,也许会对大家有一些帮助吧。

其实大家都喜欢把它和线程结合起来说明,确实他们之间的联系千丝万缕,下面就给大家介绍一下runloop这个东东吧

结合实际应用说明,有不好的地方请指教


知识点一:

ios中runloop提供了俩种:CFRunloopRefNSRunloop

CFRunloopRef : 底层的  基于C的框架,所以是线程安全的

NSRunloop:是对CFRunloopRef的封装 是面向对象的  都非线程安全的


知识点二:

runloop使用的场景:

1.大家都知道除了主线程外,都是有生命周期的,一旦线程内的所有任务完成的时候,线程会自动结束掉生命周期,自动释放掉。那么如果想要保持线程的存活,随时等待消息,那么runloop就派上用场了。


2.如果人为地去持有线程,使其不被释放,从而去重复调用[thread start],这时候程序会崩溃,给出一条这样的提示“attempt to start the thread again”。这是为什么呢,因为线程在执行完自己的任务后会进入“假死”状态,进入假死状态的线程呢,并不享有重新开启的功能  所有此时会系统报错。


3.所有这个时候就引入了“runloop”的概念,当一个线程持有了所谓的“runloop”的时候,那么它就能保持常存的状态了。


知识点:

        其实引入的runloop相当于do while的循环体,其本质如下

runloop本质分析

一直处于激活的状态,当接受到消息的时候 就进行执行相应的任务,当完成任务的时候就进入“休眠”,直到下个任务的到来。或者被通知退出。


4.线程创建时并没有自动创建runloop,而是当我们去调用的时候,这时候才会去创建,有点像我们所说的“懒加载”。


5. 主线程例外,当app运行的时候  会默认创建主线程的runloop。主线程能自动切换model。


6.runloop运行的几大条件

1.  model

mode litem

ps: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];  同时添加.


.


以上就是个人总结  有些的不好的地方 请多多指教

相关文章

网友评论

    本文标题:浅谈IOS的Runloop

    本文链接:https://www.haomeiwen.com/subject/qlbwqqtx.html