Run Loop
Run Loop就是一個事件處理的循環,用來不停的調動工作以及處理輸入事件。使用Run Loop的目的就是節省CPU效率,線程在有工作的時候忙于工作,而沒工作的時候處于休眠狀態。
一,Run Loop剖析
Structure of a Run Loop and its sources
上圖顯示了線程的輸入源
A,基于端口的輸入源(Port Sources)
B,自定義輸入源(Custom Sources)
C,Cocoa執行Selector的源("performSelector...方法" Sources)
D,定時源(Timer Sources )
線程針對上面不同的輸入源,有不同的處理機制
A,handlePort---處理基于端口的輸入源
B,customSrc---處理用戶自定義輸入源
C,mySelector---處理Selector的源
D,timerFired---處理定時源
注:線程除了處理輸入源,Run Loops也會生成關于Run Loop行為的通知(notification)。Run Loop觀察者(Run-Loop Observers)可以收到這些通知,并在線程上面使用他們來作額為的處理
===在新線程的Run Loop中注冊Observers:
---編寫一個帶有觀測者的線程加載程序
- (void)observerRunLoop
{
// 建立自動釋放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// 獲得當前thread的Run loop
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];
// 設置Run Loop observer的運行環境
CFRunLoopObserverContext context = {0, self, NULL, NULL, NULL};
// 創建Run loop observer對象
// 第一個參數用于分配該observer對象的內存
//?第二個參數用以設置該observer所要關注的的事件,詳見回調函數myRunLoopObserver中注釋
//?第三個參數用于標識該observer是在第一次進入run loop時執行還是每次進入run loop處理時均執行
//?第四個參數用于設置該observer的優先級
//?第五個參數用于設置該observer的回調函數
//?第六個參數用于設置該observer的運行環境
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if(observer)
{
// 將Cocoa的NSRunLoop類型轉換程Core Foundation的CFRunLoopRef類型
CFRunLoopRef ? = [myRunLoop getCFRunLoop];
// 將新建的observer加入到當前的thread的run loop
CFRunLoopAddObserver(cfRunLoop, observer, kCFRunLoopDefaultMode);
}
// Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode
[NSTimer scheduledTimerWithTImeInterval:0.1 target:self selector:@selector(doFireTimer:) userInfor:nil repeats:YES];
NSInteger = loopCount = 10;
do
{
// 啟動當前thread的run loop直到所指定的時間到達,在run loop運行時,run loop會處理所有來自與該run loop聯系的input sources的數據
// 對于本例與當前run loop聯系的input source只有Timer類型的source
// 該Timer每隔0.1秒發送觸發時間給run loop,run loop檢測到該事件時會調用相應的處理方法(doFireTimer:)
// 由于在run loop添加了observer,且設置observer對所有的run loop行為感興趣
// 當調用runUntilDate方法時,observer檢測到run loop啟動并進入循環,observer會調用其回調函數,第二個參數所傳遞的行為時kCFRunLoopEntry
// observer檢測到run loop的其他行為并調用回調函數的操作與上面的描述相類似
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSiceNow:1.0]];
// 當run loop的運行時間到達時,會退出當前的run loop,observer同樣會檢測到run loop的退出行為,并調用其回調函數,第二個參數傳遞的行為是kCFRunLoopExit.
--loopCount;
}while(loopCount);
//?釋放自動釋放池
[pool release];
}
===observer的回調函數:
void myRunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
switch(activity)
{
// The entrance of run loop, before entering the event processing loop.
//?This activity occurs once for each call to CFRunLoopRun / CFRunLoopRunInMode
case kCFRunLoopEntry:
NSLog(@"run loop entry");
break;
// Inside the event processing loop before any timers are processed
case kCFRunLoopBeforeTimers:
NSLog(@"run loop before timers");
break;
// Inside the event processing loop before any sources are processed
case kCFRunLoopBeforeSources:
NSLog(@"run loop before sources");
break;
// Inside the event processing loop before the run loop sleeps, waiting for a source or timer to fire
// This activity does not occur if CFRunLoopRunInMode is called with a timeout of o seconds
// It also does not occur in a particular iteration of the event processing loop if a version 0 source fires
case kCFRunLoopBeforeWaiting:
NSLog(@"run loop before waiting");
break;
//?Inside the event processing loop after the run loop wakes up, but before processing the event that woke it up
//?This activity occurs only if the run loop did in fact go to sleep during the current loop
case kCFRunLoopAfterWaiting:
NSLog(@"run loop after waiting");
break;
// The exit of the run loop, after exiting the event processing loop
// This activity occurs once for each call to CFRunLoopRun and CFRunLoopRunInMode
case kCFRunLoopExit:
NSLog(@"run loop exit");
break;
/*
A combination of all the preceding stages
case kCFRunLoopAllActivities:
break;
*/
default:
break;
}
}
1,Run Loop模式---是所有要監測的輸入源和定時源以及要通知的run loop注冊觀察者的集合。在run loop運行過程中,只有和模式相關的源才會被監測并允許他們傳遞事件消息。相反,沒有被添加的輸入源將會被過濾。
可以自定自己的Run Loop模式,但是每個模式必須有一個或者多個輸入源,定時源或者run loop的觀察者,否則,run loop直接退出,Run Loop模式將沒有意義。
另,模式區分基于事件的源而非事件的種類。例如,不能使用模式只選擇處理鼠標按下或者鍵盤事件。可以使用模式監聽端口,而暫停定時器或者改變其他源或者當前模式下處于監聽狀態run loop觀測著。
表1-1列出了cocoa和Core Foundation預先定義的模式。
2,Run Loop的輸入源
A,基于端口的輸入源
通過內置的端口相關的對象和函數,創建配置基于端口的輸入源。相關的端口函數---CFMachPort/CFMessagePortRef/CFSocketRf
B,自定義輸入源
自定義輸入源使用CFRunLoopSourceRef對象創建,它需要自定義自己的行為和消息傳遞機制
C,Cocoa執行Selector的源
和基于端口的源一樣,執行Selector的請求會在目標線程上序列化,減緩在線程上允許許多各方法容易引起的同步問題。兩者區別:一個Selector執行完成后會自動從Run Loop上移除。
Table:Performing selectors on other threads
MethodsDescription
Performs the specified selector on the
application’s main thread during that
thread’s next run loop cycle. These
methods give you the option of blocking
the current thread until the selector is
performed.
Performs the specified selector on any
thread for which you have an?NSThread
object. These methods give you the
option of blocking the current thread
until the selector is performed.
Performs the specified selector on the
current thread during the next run loop
cycle and after an optional delay period.
Because it waits until the next run loop
cycle to perform the selector, these
methods provide an automatic mini
delay from the currently executing code.
Multiple queued selectors are performed
one after another in the order they were
queued.
D,定時源
在預設的時間點同步方式傳遞消息,定時器是線程通知自己做某事的一種方法。
E,Run Loop觀察者
源是合適的同步/異步事件發生時觸發。觀察者則是在Run Loop本身運行的特定時候觸發。觀察者觸發的相關事件(參考上面紅色程序里面的函數:myRunLoopObserves(...))
1)Run Loop入口
2)Run Loop何時處理一個定時器
3)Run Loop何時處理一個輸入源
4)Run Loop何時進入休眠狀態
5)Run Loop何時被喚醒,但在喚醒之前要處理的事件
6)Run Loop終止
注:1,觀察者通過CFRunLoopObserverRef對象創建的
2,觀察者會在相應事件發生之前傳遞消息,所以通知的時間和事件實際發生的時間之間肯定有誤差
F,Run Loop 的事件隊列--包括觀察者的事件隊列
1)通知觀察者Run Loop已經啟動
2)通知觀察者任何即將要開始的定時器
3)通知觀察者任何即將啟動的非基于端口的輸入源
4)啟動任何準備好的非基于端口的源
5)如果基于端口的源準備好了并處于等待狀態,立即啟動;并進入步驟9
6)通知觀察者線程進入休眠
7)將線程置于休眠直到下面任一事件發生
A)某一事件到達基于端口的源
B)定時器啟動
C)Run Loop設置的時間已經超時
D)Run Loop被顯式喚醒
8)通知觀察者線程被喚醒
9)處理未處理的事件
A)如果用戶定義的定時器啟動,處理定時器事件并重啟Run Loop,進入步驟2
B)如果輸入源啟動,傳遞相應消息
C)如果Run Loop被顯式喚醒而且時間還沒有超時,重啟Run Loop,進入步驟2
10)通知觀察者Run Loop結束