RunLoop

  • 與線程關係 :runloop依賴於線程,每一個線程都有一個與之對應的runloop,子線程的runloop需要手動啓動,當線程結束,runloop也會結束
  • 運行
    [[NSRunLoop currentRunLoop]run];
    [loop1 runUntilDate:[NSDate date]];//[NSDate date]一直運行
    [loop2 runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:20]];
    // 運行一次runloop 直到 時間
    [[NSRunLoop currentRunLoop]acceptInputForMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:100]];
  • 停止
    1,上面有截止時間的到時間會停止運行
    2, // 停止當前線程循環
    CFRunLoopStop(CFRunLoopGetCurrent());
  • runloop添加
    1,端口源 runloop a和b交互
    // 創建端口
    NSPort *port = [[NSPort alloc]init];
    // 設置代理
    port.delegate = self;
    // 主線程添加接受消息端口 port
    [[NSRunLoop currentRunLoop]addPort:port forMode:NSRunLoopCommonModes];

    //開啓子線程 傳入端口 port
    [NSThread detachNewThreadSelector:@selector(aa:) toTarget:self withObject:port ];
    }
    /// port代理方法

  • (void)handlePortMessage:(NSPortMessage *)message{
    NSLog(@”%@”,message);
    }
    /// 子線程方法體
    -(void)aa:(id)sender{
    // 接受端口port
    NSPort *p = sender;
    // 通過接收的端口 向已經註冊此端口的 runloop 發送消息
    [p sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1] components:nil from:[NSPort port] reserved:0];

}
2,定時源 nstimer添加
3,cocoSelect源 perform。。。onThread 等事件

  • runloop觀察者
    源是在合適的同步或異步事件發生時觸發,而run loop觀察者則是在run loop本身運行的特定時候觸發。你可以使用run loop觀察者來爲處理某一特定事件或是進入休眠的線程做準備。你可以將run loop觀察者和以下事件關聯:

    1. Runloop入口

    2. Runloop何時處理一個定時器

    3. Runloop何時處理一個輸入源

    4. Runloop何時進入睡眠狀態

    5. Runloop何時被喚醒,但在喚醒之前要處理的事件

    6. Runloop終止

和定時器類似,在創建的時候你可以指定run loop觀察者可以只用一次或循環使用。若只用一次,那麼在它啓動後,會把它自己從run loop裏面移除,而循環的觀察者則不會。定義觀察者並把它添加到run loop,只能使用Core Fundation。下面的例子演示瞭如何創建run loop的觀察者:

  • (void)addObserverToCurrentRunloop

{

// The application uses garbage collection, so noautorelease pool is needed.

NSRunLoop*myRunLoop = [NSRunLoop currentRunLoop];



// Create a run loop observer and attach it to the runloop.

CFRunLoopObserverContext  context = {0, self, NULL, NULL, NULL};

CFRunLoopObserverRef observer =CFRunLoopObserverCreate(kCFAllocatorDefault,

                                                          kCFRunLoopBeforeTimers, YES, 0, &myRunLoopObserver, &context);



if (observer)

{

    CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];

   CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);

}

}

其中,kCFRunLoopBeforeTimers表示選擇監聽定時器觸發前處理事件,後面的YES表示循環監聽。

2.3 RunLoop的事件隊列

每次運行run loop,你線程的run loop對會自動處理之前未處理的消息,並通知相關的觀察者。具體的順序如下:

1. 通知觀察者run loop已經啓動
2. 通知觀察者任何即將要開始的定時器
3. 通知觀察者任何即將啓動的非基於端口的源
4. 啓動任何準備好的非基於端口的源
5. 如果基於端口的源準備好並處於等待狀態,立即啓動;並進入步驟9。
6. 通知觀察者線程進入休眠
7. 將線程置於休眠直到任一下面的事件發生:
    * 某一事件到達基於端口的源
    * 定時器啓動
    * Run loop設置的時間已經超時
    * run loop被顯式喚醒
8. 通知觀察者線程將被喚醒。
9. 處理未處理的事件
    * 如果用戶定義的定時器啓動,處理定時器事件並重啓run loop。進入步驟2
    * 如果輸入源啓動,傳遞相應的消息
    * 如果run loop被顯式喚醒而且時間還沒超時,重啓run loop。進入步驟2
10. 通知觀察者run loop結束。

*
因爲定時器和輸入源的觀察者是在相應的事件發生之前傳遞消息,所以通知的時間和實際事件發生的時間之間可能存在誤差。如果需要精確時間控制,你可以使用休眠和喚醒通知來幫助你校對實際發生事件的時間。

因爲當你運行run loop時定時器和其它週期性事件經常需要被傳遞,撤銷run loop也會終止消息傳遞。典型的例子就是鼠標路徑追蹤。因爲你的代碼直接獲取到消息而不是經由程序傳遞,因此活躍的定時器不會開始直到鼠標追蹤結束並將控制權交給程序。

Run loop可以由run loop對象顯式喚醒。其它消息也可以喚醒run loop。例如,添加新的非基於端口的源會喚醒run loop從而可以立即處理輸入源而不需要等待其他事件發生後再處理。

從這個事件隊列中可以看出:

①如果是事件到達,消息會被傳遞給相應的處理程序來處理, runloop處理完當次事件後,run loop會退出,而不管之前預定的時間到了沒有。你可以重新啓動run loop來等待下一事件。

②如果線程中有需要處理的源,但是響應的事件沒有到來的時候,線程就會休眠等待相應事件的發生。這就是爲什麼run loop可以做到讓線程有工作的時候忙於工作,而沒工作的時候處於休眠狀態。

2.4什麼時候使用run loop

僅當在爲你的程序創建輔助線程的時候,你才需要顯式運行一個run loop。Run loop是程序主線程基礎設施的關鍵部分。所以,Cocoa和Carbon程序提供了代碼運行主程序的循環並自動啓動run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)作爲程序啓動步驟的一部分,它在程序正常啓動的時候就會啓動程序的主循環。類似的,RunApplicationEventLoop函數爲Carbon程序啓動主循環。如果你使用xcode提供的模板創建你的程序,那你永遠不需要自己去顯式的調用這些例程。

對於輔助線程,你需要判斷一個run loop是否是必須的。如果是必須的,那麼你要自己配置並啓動它。你不需要在任何情況下都去啓動一個線程的run loop。比如,你使用線程來處理一個預先定義的長時間運行的任務時,你應該避免啓動run loop。Run loop在你要和線程有更多的交互時才需要,比如以下情況:

1. 使用端口或自定義輸入源來和其他線程通信
2. 使用線程的定時器
3. Cocoa中使用任何performSelector…的方法
4. 使線程週期性工作

*
如果你決定在程序中使用run loop,那麼它的配置和啓動都很簡單。和所有線程編程一樣,你需要計劃好在輔助線程退出線程的情形。讓線程自然退出往往比強制關閉它更好。

發佈了50 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章