首先看兩個runloop的樣本,來源:http://paste.lisp.org/display/86524
第一個:
- #include <CoreFoundation/CoreFoundation.h>
-
- static void
- _perform(void *info __unused)
- {
- printf("hello\n");
- }
-
- static void
- _timer(CFRunLoopTimerRef timer __unused, void *info)
- {
- CFRunLoopSourceSignal(info);
- }
-
- int
- main()
- {
- CFRunLoopSourceRef source;
- CFRunLoopSourceContext source_context;
- CFRunLoopTimerRef timer;
- CFRunLoopTimerContext timer_context;
-
- bzero(&source_context, sizeof(source_context));
- source_context.perform = _perform;
- source = CFRunLoopSourceCreate(NULL, 0, &source_context);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
-
- bzero(&timer_context, sizeof(timer_context));
- timer_context.info = source;
- timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 1, 0, 0, _timer, &timer_context);
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
-
- CFRunLoopRun();
-
- return 0;
- }
第二個:
- #include <dispatch/dispatch.h>
- #include <stdio.h>
-
- int
- main()
- {
- dispatch_source_t source, timer;
-
- source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
- dispatch_source_set_event_handler(source, ^{
- printf("hello\n");
- });
- dispatch_resume(source);
-
- timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
- dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC, 0);
- dispatch_source_set_event_handler(timer, ^{
- dispatch_source_merge_data(source, 1);
- });
- dispatch_resume(timer);
-
- dispatch_main();
- }
功能是向main線程中加入兩個input source,一個是timer,一個是自訂input source,然後這個timer中觸發自訂source,於是調用其回調方法。 在這兒timer觸發source來調用回調方法,顯得有點多此一舉。但是在多線程開發當中,這就很有用了,我們可以把這個自訂的source加入到子線程的runloop中,然後在主線程中觸發source,這樣在子線程中就可以調用回調方法了。 這樣做的好久是什麼呀? 節約用電,因為runloop一般情況下是休眠的,只有事件觸發的時候才開始工作。 這與windows下的waitforsingleobject有點類似, 與多線程中的訊號量,事件也有些雷同。
上面說到的input source(輸入源例)到底是什麼呢?輸入源範例可能包括使用者輸入裝置(如點擊button)、網路連結(socket收到資料)、定期或時間延遲事件(NSTimer),還有非同步回調(NSURLConnection的非同步請求)。然後我們對其進行了分類,有三類可以被runloop監控,分別是sources、timers、observers。
在蘋果文檔中對runloop有詳細介紹,下面參考中有中文版。那文檔中的代碼關於NSPort的部份在iOS上是不行的,不過可以用其CF方法實現,在我的demo中有展示。
每一個線程都有自己的runloop, 主線程是預設開啟的,建立的子線程要手動開啟,因為NSApplication 只啟動main applicaiton thread。
沒有source的runloop會自動結束。
事件由NSRunLoop 類處理。
RunLoop監視作業系統的輸入源,如果沒有事件數目據, 不消耗任何CPU 資源。
如果有事件數目據,run loop 就發送訊息,通知各個對象。
用 currentRunLoop 獲得 runloop的 reference
給 runloop 發送run 訊息啟動它。
文檔中介紹下面四種情況是使用runloop的場合:
1.使用連接埠或自訂輸入源和其他線程通訊
2.子線程中使用了定時器
3.cocoa中使用任何performSelector到了線程中運行方法
4.使線程履行週期性任務,(我把這個理解與2相同)
如果我們在子線程中用了NSURLConnection非同步請求,那也需要用到runloop,不然線程退出了,相應的delegate方法就不能觸發。
解決的方法參看:
http://www.cocoabyss.com/foundation/nsurlconnection-synchronous-asynchronous/
http://www.wim.me/nsurlconnection-in-its-own-thread/
具體參看demo.
參考:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
http://www.wim.me/nsurlconnection-in-its-own-thread/
http://xubenyang.me/384
http://iphonedevelopmentbits.com/event-driven-multitasking-runloopssymbian-ios