iOS中RunLoop机制的探索

本文详细探讨了iOS中的RunLoop机制,包括其基本概念如NSRunLoop对象、RunLoopMode、事件源和观察者,以及内部的基本流程和系统如何利用RunLoop处理事件,如界面更新、事件响应。此外,还介绍了RunLoop在日常开发中的应用,如线程池和监听RunLoop处理事件。
摘要由CSDN通过智能技术生成

RunLoop是ios中一个非常重要的机制,ios系统底层很多模块都是通过RunLoop机制实现的,例如界面更新、事件响应等。本质上RunLoop是一种用于循环处理事件,而又不至于使CPU无意义空转的方式。

一、基本概念(了解过的可以跳过这一节)

1、NSRunLoop对象

这里写图片描述

(1)CFRunLoopRef

NSRunLoop对象是OC对象,是对CFRunLoopRef的封装,可以通过getCFRunLoop方法获取其对应的CFRunLoopRef对象。注意,NSRunLoop不是线程安全的,但CFRunLoopRef是线程安全的。

(2)RunLoopMode

NSRunLoop对象是一系列RunLoopMode的集合,每个mode包括有这个模式下所有的Source源、Timer源和观察者。每次RunLoop调用的时候都只能调用其中的一个mode,接收这个mode下的源,通知这个mode下的观察者。这样设计的主要目的就是为了隔离各个模式下的源和观察者,使其不相互影响。

其中系统默认注册的5个mode有:

  • kCFRunLoopDefaultMode : App默认的mode,一般情况下App都是运行在这个mode下的。
  • UITrackingRunLoopMode : 界面跟踪时的mode,一般用于ScrollView滚动的时候追踪的,保证滑动的时候不受其他事件影响。
  • UIInitializationRunLoopMode : 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
  • GSEventReceiveRunLoopMode : 接受系统事件的内部 Mode,一般用不到。
  • kCFRunLoopCommonModes : 占位mode,可以向其中添加其他mode用以检测多个mode的事件
(3)CFRunLoopSourceRef

CFRunLoopSourceRef是事件源产生的地方,主要有两种:

  • Source0 :只包含一个函数指针(回调方法),不能自动触发,只能手动触发,触发方式是先通过CFRunLoopSourceSignal(source)将这个Source标记为待处理,然后再调用CFRunLoopWakeUp(runloop) 来唤醒RunLoop处理这个事件。

  • Source1 :基于port的Source源,包含一个port和一个函数指针(回调方法)。该Source源可通过内核和其他线程相互发送消息,而且可以主动唤醒RunLoop。

(4)CFRunLoopTimerRef

CFRunLoopTimerRef是基于事件的触发器,其中包含一段时间长度、延期容忍度和一个函数指针(回调方法)。当其加入到RunLoop中时,RunLoop会注册一个时间点,当到达这个时间点后,会触发对应的事件。

(5)performSEL

performSEL其实和NSTimer一样,是对CFRunLoopTimerRef的封装。因此,当调用performSelecter:afterDelay: 后,实际上内部会转化成CFRunLoopTimerRef并添加到当前线程的RunLoop中去,因此,如果当前线程中没有启动RunLoop的时候,该方法会失效。

(6)CFRunLoopObserverRef

CFRunLoopObserverRef是RunLoop的观察者。每个观察者都可以观察RunLoop在某个模式下事件的触发并处理。可以观察的时间点包括以下几点:

  • kCFRunLoopEntry:即将进入RunLoop
  • kCFRunLoopBeforeTimers:即将处理Timer
  • kCFRunLoopBeforeSources:即将处理Source
  • kCFRunLoopBeforeWaiting:即将进入休眠
  • kCFRunLoopAfterWaiting:刚从休眠中被唤醒
  • kCFRunLoopExit:即将退出RunLoop
(7)modeItem

上面的Source/Timer/Observer被统称为mode item,一个item可以被同时加入多个mode。但一个item被重复加入同一个mode时是不会有效果的。如果一个mode中一个item都没有,则RunLoop会直接退出,不进入循环。

2、RunLoop的驱动

 BOOL isRunning = NO;
 do {
        isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]];
 } while (isRunning);

RunLoop本身是不能循环的,要通过外部的while循环循环驱动。

3、RunLoop内部的基本流程

每次运行RunLoop,内部都会处理之前没有处理的消息,并且在各个阶段通知相应的观察者。大致步骤如下:

(1)通知观察者RunLoop启动
(2)通知观察者即将处理Timer
(3)通知观察者即将处理Source0
(4)触发Source0回调
(5)如果有Source1(基于port)处于ready状态,直接处理该Source1然后跳转到()去处理消息
(6)如果没有待处理消息,则通知观察者RunLoop所在线程即将进入休眠。
(7)休眠前,RunLoop会添加一个dispatchPort,底层调用mach_ msg接收mach_ port的消息。线程进入休眠,直到下面某个事件触发唤醒线程
  • 基于port的Source1事件到达
  • Timer时间到达
  • RunLoop启动时设置的最大超时时间到了
  • 手动唤醒
(8)唤醒后,将休眠前添加的dispatchPort移除,并通知观察者RunLoop已经被唤醒
(9)通过handle_ msg处理消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值