iOS系統(tǒng)中有兩個關(guān)于Runloop的對象:NSRunLoop 和 CFRunLoopRef
NSRunloop是Foundation框架提供的,是對CoreFoundation框架提供的CFRunloopRef的封裝。
CoreFoundation提供的是純C語言的API,都是線程安全的,F(xiàn)oundation不是線程安全的。
iOS中RunLoop是開源的,在CoreFoundation的開源代碼中,可以在這里下載:RunLoop源碼
CFRunLoopMode
RunLoop在同一時段內(nèi)只能并且必須在一種特定的Mode下run,這個Mode被稱為currentMode
如果想更換當(dāng)前的mode,需要暫停當(dāng)前的loop,然后重啟新的loop,這樣可以分離開不同的Source/Timer/Observer,互不影響
可以定義自己的mode,但是幾乎沒有這么做的
NSDefaultRunLoopMode
默認(rèn)的狀態(tài),空閑時候的狀態(tài)
-
UITrackingRunLoopMode
滑動跟蹤,滑動scrollView的時候處于這個狀態(tài),保證界面滑動時不受其他界面的影響 -
UIInitializationRunLoopMode
私有的mode,App啟動的時候的狀態(tài),加載出第一個頁面后,就轉(zhuǎn)成了Default -
NSRunLoopCommonModes
默認(rèn)的是包括Default
和Tracking
兩個狀態(tài),也可以自定義的往里面添加 -
GSEventReceiveRunLoopMode
接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
來看一下CFRunLoopMode的構(gòu)成
可以看到,一個Runloop可以包含如果個Mode,每個Mode又包含了多個Source/Timer/Observer
CFRunLoopSource
CFRunLoopSource
是RunLoop的數(shù)據(jù)源抽象類,類似Objective-C中的協(xié)議protocol,實現(xiàn)了這個protocol就可以充當(dāng)RunLoop的數(shù)據(jù)源(幾乎沒有這么做的),RunLoop自己定義了兩個Source:Source0和Source1
- Source0 : 處理App內(nèi)部事件,比如屏幕響應(yīng)UIEvent, CFSocket,我們點(diǎn)擊屏幕就是Source0事件
- Source1 : 由內(nèi)核管理,比如mach_port
注意
:mach_port是iOS系統(tǒng)中進(jìn)程間通信的一種方式,如果進(jìn)程1往一個port中發(fā)送一個消息,此時進(jìn)程2監(jiān)聽了這個port,就會拿到這個消息
NSPort是對CoreFoundation中的CFMachPort和CFMessagePort的封裝
來看一下對source事件的定義
CFRunLoopSource
中用union確保這個source要么是source0,要么是source1看一下source0和source1的具體定義:
souce0中定義的都是函數(shù)指針
source1中出了函數(shù)指針,還有一個mach_port
CFRunLoopObserver
CFRunLoopObserver
相當(dāng)于觀察者模式的觀察者,用來向觀察者報告RunLoop當(dāng)前的狀態(tài)
下面是RunLoop中定義的狀態(tài):
我們可以用下面的代碼創(chuàng)建一個CFRunLoopObserver,添加到當(dāng)前線程的RunLoop中,監(jiān)聽當(dāng)前線程Runloop的當(dāng)前狀態(tài)
例如應(yīng)用啟動的時候,會處理一系列的source事件和timer事件,當(dāng)處理完之后,就會打印出
即將進(jìn)入 休眠
,此時,我們點(diǎn)擊屏幕,會打印出剛從休眠中喚醒
,然后再處理一系列的source和timer事件,然后再次進(jìn)入休眠狀態(tài)。
CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即將進(jìn)入 loop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即將處理 timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即將處理 source");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即將進(jìn)入 休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"剛從休眠中喚醒");
break;
case kCFRunLoopExit:
NSLog(@"即將退出 loop");
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopDefaultMode);
CFRelease(observe);
CFRunLoopObserver 與 AutoreleasePool
AutoreleasePool在RunLoop的兩次sleep之間對AutoreleasePool進(jìn)行pop和push,將這次loop中產(chǎn)生的Autorelease對象進(jìn)行釋放
RunLoop的運(yùn)行機(jī)制
關(guān)于運(yùn)行機(jī)制,先來看兩幅圖:
上面的兩張圖的運(yùn)行循環(huán)的邏輯,都對應(yīng)著源碼中的__CFRunLoopRun這個函數(shù)