Runloop 之重读 ibireme

重读 ibireme

参考

RunLoop 的概念
  • 一个线程一次只能处理一个任务,处理完线程就销毁了。我们希望线程能随时处理事件,且不退出。
function loop() {
	initialize();
	do {
		var message = get_next_message();
		processs_message(message);
	} while (message != quit)
}

实现这种模型的关键是:在 message 不为 quit 时,如何管理事件/消息,让线程在没有处理消息时休眠以避免资源占用,在有消息到来时唤醒

  • runloop 实际上就是一个对象,用来管理事件和消息,并提供了一个进入 event loop 的函数入口。一旦线程进入入口就一直处于该函数体内执行“接受消息->处理消息->等待”循环中,直到这个循环结束(比如传入 quit 消息),函数返回。

  • 苹果提供了两个 event loop 这样的对象:NSRunloop,CFRunloopRef。CFRunloopRef 是面向 C 的 CoreFoundation 框架的 API,API 是线程安全的。NSRunloop 是面向 OC 的 Foundation 框架的 API,API 不是线程安全的。

RunLoop 与线程的关系
  • 线程刚创建时并没有 runloop,如何你不主动获取,那它一直都不会有。
  • 苹果没有提供创建 runloop 的 API,只提供了两个获取 runloop 的 API: CFRunloopGetMain(), CFRunloopGetCurrent()。
  • 在线程里创建 runloop 时,会将线程作为 key,runloop 对象作为 value 保存在一个全局 loopsDic 里。
  • 线程结束时,会销毁该 runloop 对象。
  • 从源码里看,不管是创建一个 runloop,还是获取 runloop,线程都要作为参数。所以只能在当前线程里获取 runloop(除 main run loop 外)。
Runloop 对外接口
  • 在 CoreFoundation 里关于 Runloop 有5个类:

    • CFRunloopRef
    • CFRunloopModeRef
    • CFRunloopSourceRef
    • CFRunloopObserverRef
    • CFRunloopTimerRef

    其中 CFRunloopModeRef 类并没有对外暴露,只是通过 CFRunloopRef 接口进行了封装。

    它们的关系是:一个 runloop 可以包含多个 mode,每个 mode 又包含多个 source, observer, timer。每次运行 runloop 时只能指定一个 mode,所以各 mode 的 source 等不受影响。

  • CFRunLoopSourceRef 事件产生的地方

  • CFRunloopTimerRef 是基于时间的触发器,当 timer 加入到 runloop 时,runloop 会注册对应的时间,时间一到,唤醒 runloop 执行它的回调。

  • 如果一个 mode 中一个 item 也没有,则 runloop 会直接退出,不进入循环。

runloop mode
  • 苹果支持5中 mode

    • NSDefaultRunLoopMode
    • NSEventTrackingRunLoopMode
    • NSRunLoopCommonModes
    • NSConnectionReplyMode:cocoa用这个模式结合 NSConnection 对象监测回调。
    • NSModalPanelRunLoopMode
  • 程序启动后,系统注册了5个 mode

    • kCFRunLoopDefaultMode
    • UITrackingRunLoopMode
    • kCFRunLoopCommonModes
    • UIInitializationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
    • GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到。
苹果用 Runloop 实现的功能
PerformSelecter
  • 当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。
  • 当调用 performSelector:onThread: 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。
关于网络请求

通常使用 NSURLConnection 时,我们会传入一个 delegate,并调用 [connection start]。start API 里会主动获取当前的 runloop,并在 defaultMode 里创建几个 source0,其中一个 source0 用来处理 delegate 回调。

当开始网络传输时,NSURLConnection 会创建两个线程:URLConnectionLoader 线程和 CFSocket 线程。CFSocket 线程用来处理底层 socket 连接,URLConnectionLoader 线程会在其 runloop 中监听 socket 线程 port 消息,若有消息会通知 delegate 线程注册的 source0 事件并处罚 delegate 回调。

socket 线程通过 mach port 通知 urlconnectionloader 的 runloop;urlconnectionloader 通过标记 delegate 线程的 source0 事件为待处理状态,并 wakeup delegate 线程 runloop 处理 delegate 回调。

我觉得是 urlconnectloader 持有 source0,并 add source0 到 delegate runloop default mode 里。

在这里插入图片描述

Mach Port

参考
所有的进程间通信都最终依赖于 Mach 内核 API 提供的功能。

Mach Port 就是一个轻量级的,功能强大的 API,来接受和发送消息,通过 mach_msg(port) 。

幸运地是:CoreFoundation 和 Foundation 提供了更高级别的 Mach port:CFMachPort,NSMachPort。且它们可以被用在 runloop source 上。(CFMessagePort / NSMessagePort 有利于两个端口间同步通信)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值