RunLoop

1. 简介
RunLoop可以理解为一个do-while循环:

function loop(){
	initialize();
	do{
		event = getEvent();
		HandleEvent(event);
		}while(isWeakUp);
	}

当然肯定还有一些其他的处理逻辑在内部,才能保证其一一对应的线程能够在有任务时被唤醒,没有任务时休眠。
OSX/iOS系统提供了两个RunLoop对象:NSRunLoopCFRunLoopRef;

CFRunLoopRef是苹果提供的CoreFoundation框架内一套纯c的函数API,而且API的线程都是安全的;
NSRunLoop则是基于CFRunLoopRef的封装,提供了面向对象的API,但是线程是不安全的。

首先看一下RunLoop结构体

struct __CFRunLoop {
    CFRuntimeBase _base;
    _CFRecursiveMutex _lock;			/* locked for accessing mode list */
    __CFPort _wakeUpPort;			// used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    _CFThreadRef _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;//set
    CFMutableSetRef _commonModeItems;//set
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes; //set
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
    _Atomic(uint8_t) _fromTSD;
    CFLock_t _timerTSRLock;
};

主要是_wakeUpPort、_commonModes、_commonModeItems、_currentMode

_wakeUpPort:是指当前唤醒runloop的端口
_commonModes:是一个集合,存放的是当前runloop所有Mode,而每个Mode包含若干个 Source/Timer/Observer
_commonModeItems:这个集合主要存放的是唤醒runloop的事件源:timer、source(分为source0,source1)、observer;
_currentMode:每次调用RunLoop的主函数,只能指定其中的一个Mode,故当前被指定的Mode就是_currentMode,RunLoop如需切换Mode只能退出当前loop重新指定Mode再进入
引用自

source0和source1的区别
source0不具备唤醒线程的能力,只包含一个回调,不能主动触发事件,使用时需先调用CFRunLoopSourceSignal(source),将这个source标记为待处理,然后手动调用 CFRunLoopWeakUp(runnloop)来唤醒RunLoop,让其处理这个事件。
source1:则能够主动唤醒线程,包含了一个mach_port和一个回调(函数指针),被用于通过内核和其他线程相互发送消息,这种Source能主动唤起RunLoop的线程。内核端口mach_port可以直接唤醒RunLoop,如触摸事件。

系统默认注册了5个mode
1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行的。
2. UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
3. UIInitializationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后就不再使用
4. GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到。
5. kCFRunLoopCommonModes,这是一个占位的Mode,没有实际作用
2. RunLoop实现逻辑
根据苹果在文档(https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW23)里的说明,RunLoop内部逻辑大致如下:

  1. Notify observers that the run loop has been entered.
  2. Notify observers that any ready timers are about to fire.
  3. Notify observers that any input sources that are not port based are about to fire.
  4. Fire any non-port-based input sources that are ready to fire.
  5. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
  6. Notify observers that the thread is about to sleep.
  7. Put the thread to sleep until one of the following events occurs:
    • An event arrives for a port-based input source.
    • A timer fires.
    • The timeout value set for the run loop expires.
    • The run loop is explicitly woken up.
  8. Notify observers that the thread just woke up.
  9. Process the pending event.
    • If a user-defined timer fired, process the timer event and restart the loop. Go to step 2.
    • If an input source fired, deliver the event.
    • If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2.
  10. Notify observers that the run loop has exite

流程图引用自(https://blog.ibireme.com/wp-content/uploads/2015/05/RunLoop_1.png)
流程图引用自
3. 什么时候使用RunLoop

When Would You Use a Run Loop? The only time you need to run a run
loop explicitly is when you create secondary threads for your
application.

Thread Safety and Run Loop Objects Thread safety varies depending on
which API you are using to manipulate your run loop. The functions in
Core Foundation are generally thread-safe and can be called from any
thread. If you are performing operations that alter the configuration
of the run loop, however, it is still good practice to do so from the
thread that owns the run loop whenever possible.

The Cocoa NSRunLoop class is not as inherently thread safe as its Core
Foundation counterpart. If you are using the NSRunLoop class to modify
your run loop, you should do so only from the same thread that owns
that run loop. Adding an input source or timer to a run loop belonging
to a different thread could cause your code to crash or behave in an
unexpected way.

摘自苹果文档,可以看出,主要用于线程间的通信。

细节还是需要自己去下载源码看了,重在自己领悟。源码链接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章