Runloop 解释笔记
2015-05-06 14:54
225 查看
Runloop是iOS开发的一个难点,需要不断体会才能真正理解。
一个runloop有几个要素组成,一个是runloop模式,一个是source,一个是observer。runloop模式是需要监视的source和observer的集合,runloop运行期间,只有和该模式相关的source才会被监视并允许传递事件,也只允许相关的observer会被通知runloop的进程。 source分input source和timer source。
input source:
基于端口的输入源
自定义输入源
Cocoa执行selector的源
timer source:
timer source(定时源)
务必记住一点,输入源和观察者都是依赖于当前runloop的模式的。模式不对,是不会触发它们工作的。
timer创建
NSTimer的运行是依赖于runloop的。可以通过以下三种方式创建:
使用 scheduledTimerWithTimeInterval:invocation:repeats: 或者scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 这两个类方法创建一个timer并把它指定到一个默认的runloop模式中
使用 timerWithTimeInterval:invocation:repeats: 或者 timerWithTimeInterval:target:selector:userInfo:repeats:这两个类方法创建一个timer的对象,不把它知道那个到run loop. (当创建之后,你必须手动的调用NSRunLoop下对应的方法 addTimer:forMode: 去将它制定到一个runloop模式中.)
使用 initWithFireDate:interval:target:selector:userInfo:repeats: 方法分配并创建一个NSTimer的实例 (当创建之后,你必须手动的调用NSRunLoop下对应的方法addTimer:forMode: 去将它制定到一个runloop模式中.)
(以上引用自http://www.cnblogs.com/ios-wmm/archive/2012/08/24/2654779.html)
前面两种方式创建的timer可能不能满足我们的需要,这时候,我们还得执行addTimer:forMode:加到正确的runloop中去。
timer启动
timer的启动有两个条件,一是当前为匹配的runloop mode,二是fire date到来。不管如何你可以先调用fire来等待runloop进入匹配的模式。或者可以设置fireDate,这都是类似的。设置不同的fireDate,在不同时期会有不同的意义。
[time setFireDate:[NSDate distantFuture]] 很显然这个一直不会fire,如果正在运行中的话,就相当于暂停了。
[time setFireDate:[NSDate distanPast]] 这个是已经fire了。
[timer setFireDate:[NSDate date]] 这个表示继续,和fire其实是一样的。和上面的没有什么区别。
除了设置fireDate,timer创建的时候设定的timeinterval也是另一种触发方式。
timer的销毁
timer要从runloop中销毁,要使用invalidate方法:
这里还有个可能会碰到的问题,滚动tableview时候,NSTimer似乎卡住了。原因是当前的runloop的mode发生了改变,运行在默认模式上的source没有被触发。可参考http://blog.csdn.net/jasonblog/article/details/7854693。
core foundation版本:
说完timer,就要说说input source。自定义的先不管,先说基于端口和Cocoa的selector源。首先他们都是iOS进程间通信的方式。现在我们很少使用前者,推荐使用后者。
NSMachPort方式下,NSPort有3个子类,NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。(http://blog.sina.com.cn/s/blog_7815a31f0101ea0n.html)
配置一个NSMachPort相当的复杂。
首先要在当前线程创建一个Port,并设置其代理,加入runloop中。然后分配一个新的线程,在上面调用另一个类的方法,很关键的一点是要把这个Port作为参数传入。
代理要实现handlePortMessage:方法或者handleMachMessage:方法。参数是NSPortMessage,可以从它的msgid方法中获取message类型,并作出相应的处理。
再回到被调用的那个类的方法中,这时候传入了一个Port,被调用的类有责任向该Port发送message来和调用线程通信,因此要在该调用方法中调用sendCheckinMessage:
诸如之类的方法,参数是NSPort类型。当然这个方法也有义务保持自己的runloop。
sendCheckinMessage:方法中,你同样要创建自己的Port,并设置代理,实现代理方法,将Port加入运行循环。关键的一点是要创建NSPortMessage对象,连接这两个Port。创建完之后要设置msgid,并通过sendBeforeDate:方法发送出去。
配置一个NSMessagePort对象 (略)
Core Foundation版本(略)
以上详细代码可查看http://mobile.51cto.com/hot-403083_2.htm
这时候似乎是抛出创建自定义输入源的时候。自定义输入源也是一个大坑,也很复杂。简单来说,你需要创建source,并把它安装到runloop,然后发送一个signal通知源并唤醒runloop。source的创建依赖于一个context,context中指定你关心事件的回调函数。这些事件包括scheduleRoutine,performRouting和cancelRouting等。在scheduleRouting中你可以把这些source的引用保存起来便于调度。performRouting中应该是执行source的主要的任务。cancelRouting中,如果这些source被管理中的话,这个时候就可以将其从管理队列中移除。分段讲解的代码详见官方文档。
Runloop的启动
runloop有三种启动方式:
无条件的,不推荐
设置超时
特定模式
后面两种是建议的做法,当然它们不是互斥的,一个超时的也可以是在某种模式下的。
退出runloop
退出有两种方式:
超时自退
通知停止,只能使用CFRunLoopStop来实现。
尽管已出runloop中的所有输入源和定时源也可能导致runloop退出,但是文档多次强调这是不可取的,因为系统会动态加入一些source。
另外NSRunloop是非线程安全的,而CFRunloop是线程安全的。
一个runloop有几个要素组成,一个是runloop模式,一个是source,一个是observer。runloop模式是需要监视的source和observer的集合,runloop运行期间,只有和该模式相关的source才会被监视并允许传递事件,也只允许相关的observer会被通知runloop的进程。 source分input source和timer source。
input source:
基于端口的输入源
自定义输入源
Cocoa执行selector的源
timer source:
timer source(定时源)
务必记住一点,输入源和观察者都是依赖于当前runloop的模式的。模式不对,是不会触发它们工作的。
timer创建
NSTimer的运行是依赖于runloop的。可以通过以下三种方式创建:
使用 scheduledTimerWithTimeInterval:invocation:repeats: 或者scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 这两个类方法创建一个timer并把它指定到一个默认的runloop模式中
使用 timerWithTimeInterval:invocation:repeats: 或者 timerWithTimeInterval:target:selector:userInfo:repeats:这两个类方法创建一个timer的对象,不把它知道那个到run loop. (当创建之后,你必须手动的调用NSRunLoop下对应的方法 addTimer:forMode: 去将它制定到一个runloop模式中.)
使用 initWithFireDate:interval:target:selector:userInfo:repeats: 方法分配并创建一个NSTimer的实例 (当创建之后,你必须手动的调用NSRunLoop下对应的方法addTimer:forMode: 去将它制定到一个runloop模式中.)
(以上引用自http://www.cnblogs.com/ios-wmm/archive/2012/08/24/2654779.html)
前面两种方式创建的timer可能不能满足我们的需要,这时候,我们还得执行addTimer:forMode:加到正确的runloop中去。
timer启动
timer的启动有两个条件,一是当前为匹配的runloop mode,二是fire date到来。不管如何你可以先调用fire来等待runloop进入匹配的模式。或者可以设置fireDate,这都是类似的。设置不同的fireDate,在不同时期会有不同的意义。
[time setFireDate:[NSDate distantFuture]] 很显然这个一直不会fire,如果正在运行中的话,就相当于暂停了。
[time setFireDate:[NSDate distanPast]] 这个是已经fire了。
[timer setFireDate:[NSDate date]] 这个表示继续,和fire其实是一样的。和上面的没有什么区别。
除了设置fireDate,timer创建的时候设定的timeinterval也是另一种触发方式。
timer的销毁
timer要从runloop中销毁,要使用invalidate方法:
if ([scrollTimer isValid] == YES) { [scrollTimer invalidate]; scrollTimer = nil; }
这里还有个可能会碰到的问题,滚动tableview时候,NSTimer似乎卡住了。原因是当前的runloop的mode发生了改变,运行在默认模式上的source没有被触发。可参考http://blog.csdn.net/jasonblog/article/details/7854693。
core foundation版本:
RunLoopRef runLoop = CFRunLoopGetCurrent(); CFRunLoopTimerContext context = {0, NULL, NULL, NULL, NULL}; CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 0.3, 0, 0, &myCFTimerCallback, &context); CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
说完timer,就要说说input source。自定义的先不管,先说基于端口和Cocoa的selector源。首先他们都是iOS进程间通信的方式。现在我们很少使用前者,推荐使用后者。
NSMachPort方式下,NSPort有3个子类,NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。(http://blog.sina.com.cn/s/blog_7815a31f0101ea0n.html)
配置一个NSMachPort相当的复杂。
首先要在当前线程创建一个Port,并设置其代理,加入runloop中。然后分配一个新的线程,在上面调用另一个类的方法,很关键的一点是要把这个Port作为参数传入。
代理要实现handlePortMessage:方法或者handleMachMessage:方法。参数是NSPortMessage,可以从它的msgid方法中获取message类型,并作出相应的处理。
再回到被调用的那个类的方法中,这时候传入了一个Port,被调用的类有责任向该Port发送message来和调用线程通信,因此要在该调用方法中调用sendCheckinMessage:
诸如之类的方法,参数是NSPort类型。当然这个方法也有义务保持自己的runloop。
sendCheckinMessage:方法中,你同样要创建自己的Port,并设置代理,实现代理方法,将Port加入运行循环。关键的一点是要创建NSPortMessage对象,连接这两个Port。创建完之后要设置msgid,并通过sendBeforeDate:方法发送出去。
配置一个NSMessagePort对象 (略)
Core Foundation版本(略)
以上详细代码可查看http://mobile.51cto.com/hot-403083_2.htm
这时候似乎是抛出创建自定义输入源的时候。自定义输入源也是一个大坑,也很复杂。简单来说,你需要创建source,并把它安装到runloop,然后发送一个signal通知源并唤醒runloop。source的创建依赖于一个context,context中指定你关心事件的回调函数。这些事件包括scheduleRoutine,performRouting和cancelRouting等。在scheduleRouting中你可以把这些source的引用保存起来便于调度。performRouting中应该是执行source的主要的任务。cancelRouting中,如果这些source被管理中的话,这个时候就可以将其从管理队列中移除。分段讲解的代码详见官方文档。
Runloop的启动
runloop有三种启动方式:
无条件的,不推荐
设置超时
特定模式
后面两种是建议的做法,当然它们不是互斥的,一个超时的也可以是在某种模式下的。
退出runloop
退出有两种方式:
超时自退
通知停止,只能使用CFRunLoopStop来实现。
尽管已出runloop中的所有输入源和定时源也可能导致runloop退出,但是文档多次强调这是不可取的,因为系统会动态加入一些source。
另外NSRunloop是非线程安全的,而CFRunloop是线程安全的。
相关文章推荐
- 【Nginx笔记】 fastcgi_param解释 转
- Linux备用常用笔记——etc目录下主要配置文件解释
- 【深度学习】笔记3_caffe自带的第一个例子,Mnist手写数字识别所使用的LeNet网络模型的详细解释
- 《c专家编程》笔记--解释复杂的声明
- Hinton Neural Networks课程笔记2c:感知机的几何解释
- 机器学习笔记三 - 局部加权回归、最小二乘的概率解释、逻辑斯蒂回归、感知器算法
- [笔记]Python虚拟机对函数的解释
- TCP/IP具体解释学习笔记--TCP的超时与重传
- 懒得笔记1 框架理念中一些 名词 解释
- Python学习笔记0001:字符串切片问题解释
- Java笔记003——Java中缩写解释
- 辛星站点构架师笔记第五篇即缓存具体解释
- iOS学习笔记-134.RunLoop02——Runloop与线程
- 视频编解码学习笔记-----常用名词解释
- C++ UFunction({FLAG}) 宏 FLAG 解释笔记
- 面向对象笔记2 原型链 解释
- 重写hashCode中的31值得解释说明笔记
- [Obj-C笔记] "self = [super init]"的解释与潜藏bug
- OpenCV学习笔记(3):一些函数等的解释(更新中...)
- 【吴恩达Deeplearning.ai笔记一】直观解释逻辑回归