iOS RunLoop深入浅出

转载自原文,参入自己的了解。

另外有一篇比较好介绍RunLoop与NSTimer之间的关系的文章,链接在这

RunLoop解决什么问题

手机是事件驱动的架构。一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出
具体来说:
1.程序一直运行并接受用户输入。
2.决定程序何时应该处理哪些Event。
3.调节解耦 被调方和主调方解耦
4.节省CPU时间

在Cocoa中哪些用到了RunLoop

1.NSTimer
2.UIEvent
3.AutoRelease
4.NSObject(NSDelayPerforming) NSObject (NSThreadPerformAddtion)
5.CA层接口
6.GCD Dispatch_get_main_queue()
7.NSURLConnection AFNetworking

Event Loop 在很多系统和框架里都有实现,比如 Node.js 的事件处理,比如 Windows 程序的消息循环,再比如 OSX/iOS 里的 RunLoop。实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。

线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)
但是并不是说一个Thread只能起一个RunLoop,可以起很多,但是必须是嵌套的,就是一个RunLoop,里面可以再嵌套RunLoop,但是根RunLoop只有一个。

RunLoopMode是RunLoop的核心,跑RunLoop必须在固定的模式下。
CFRunLoopSource,CFRunLoopTimer,CFRunLoopObserver.
1.CFRunLoopTimer NSTimer是对CFRunLoopTimer的封装。
2.CFRunLoopSource 比较抽象的一个概念,形象讲就是数据源的抽象类。有两个version的source:source0,source1.
source0:处理app内部事件,app自己负责管理(触发)。如UIEvent,CFSocket。
source1:由RunLoop和系统内核管理,mach port驱动,如CFPort,CFMessagePort。
如有需要,自己选择一种source,来实现。
CFRunLoopObserver:向外部报告RunLoop的状态的改变,框架中很多机制都由RunLoopOberserver触发,如CAAnimation。

RunLoop 对外的接口

在 CoreFoundation 里面关于 RunLoop 有5个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

其中 CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

**CFRunLoopSourceRef **是事件产生的地方。Source有两个版本:Source0 和 Source1。

  • Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
  • Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。

CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。

CFRunLoopObserverRef是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
      kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
      kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
      kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
      kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
      kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
      kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};

RunLoopObserver 与 AutoReasePool

  • UIKit通过RunLoopObserver在RunLoop两次Sleep之间对AutoReleasePool进行Pop和Push,将这次Loop产生的内存池进行释放。

Mode是IOS滑动技术的关键苹果公开提供的 Mode 有两个:kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用这两个 Mode Name 来操作其对应的 Mode。

CFRunLoopMode

  • RunLoop在同一时间必须只能在一种Mode下run
  • RunLoop如果想更换Mode,则必须停止当前Loop

你可能感兴趣的:(iOS RunLoop深入浅出)