NSRunLoop的一點理解


一、類定義

  + (NSRunLoop *)currentRunLoop
    如果調用的線程中沒有runloop,那么將會創建一個並返回
  + (NSRunLoop *)mainRunLoop
    返回主線程的runloop

  - (void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate
    運行loop一次或者直到limitDate。如果沒有input sources加入到這個loop,那么馬上返回;否則一直運行到limitDate,或者接口到一個input source然后返回。
  - (void)addPort:(NSPort *)aPort forMode:(NSString *)mode
  - (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
    port和timer都可以添加到多個mode中
  - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(id)anArgument
    取消所有mode中的perform select,argument必須跟指定調用時候的一樣
  - (void)cancelPerformSelectorsWithTarget:(id)target
  - (NSString *)currentMode
    如果run loop沒有運行,那么返回nil
  - (CFRunLoopRef)getCFRunLoop
  - (NSDate *)limitDateForMode:(NSString *)mode
    下一次運行的時間,如果沒有指定的mode上沒有input source,返回nil
  - (void)performSelector:(SEL)aSelector target:(id)target argument:(id)anArgument order:(NSUInteger)order modes:(NSArray *)modes
order值越低優先級越高
  - (void)removePort:(NSPort *)aPort forMode:(NSString *)mode
  - (void)run
    在default mode下無限運行loop,但是如果沒有任何input source,會立即返回。手動移除所有已知的inout source並不能保證run loop停止運行,因為系統可能會添加一些input source。
  - (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate
    運行input source一次,為指定mode的input阻塞直到date的時間。如過沒有input source,立即返回並返回NO。
  - (void)runUntilDate:(NSDate *)limitDate
  如果沒有input source,立即返回。否則在limitDate到來之前,不停的循環。
再詳細的就看文檔吧

二、RunLoopMode
NSDefaultRunLoopMode 這是大多數操作中使用的模式。
NSConnectionReplyMode 該模式用來監控NSConnection對象。你通常不需要在你的代碼中使用該模式。
NSModalPanelRunLoopMode Cocoa使用該模式來標識用於modal panel(模態面板)的事件。
NSEventTracking(UITrackingRunLoopMode) Cocoa使用該模式來處理用戶界面相關的事件。
NSRunLoopCommonModes 這是一組可配置的通用模式。將input sources與該模式關聯則同時也將input sources與該組中的其它模式進行了關聯。對於Cocoa應用,該模式缺省的包含了default,modal以及event tracking模式。

  一個常見的問題就是,主線程中一個NSTimer添加在default mode中,當界面上有一些scroll view的滾動頻繁發生導致run loop運行在UItraking mode中,從而這個timer沒能如期望那般的運行。所以,我們就可以把這個timer加到NSRunLoopCommonModes中來解決(iOS中)。

三.疑團重重
來看看這張經典的圖片


其中Input source是一些異步的事件,比如port,selector等,這個會讓runUntilDate:跳出(當然指的是非主線程中的runloop)。Timer source是同步的,一個timer結束后,在重復時間后或者手動fire后才會再一次調用。

在來看看這張圖片

它說明了用戶對ui的操作實際上是一種port,會放到一個隊列中傳到loop,然后由loop交給主線程處理。loop就是一個循環,接受event,傳遞,繼續。主線程是另一個循環,負責事件的處理與界面的顯示。當然這兩者關系復雜。

在看下面的代碼

BOOL pageStillLoading = YES;
-(void)press:(id)sender
{
    [(UIButton*)sender setSelected:YES];
    NSLog(@"begin"); // 1
    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];  // 2
    NSLog(@"end"); // 3

    pageStillLoading = YES;
    [NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self withObject:nil]; // 4
    while (pageStillLoading) {
        [[NSRunLoop currentRunLoop] runMode:UITrackingRunLoopMode beforeDate:[NSDate distantFuture]]; // 5
        NSLog(@"while end");
    }
    NSLog(@"over");
    
}

-(void)loadPageInBackground:(id)sender
{
    sleep(3); // 6
    NSLog(@"timer"); // 7
    pageStillLoading = NO; // 8
}

 

我在viewcontroller的view上加了一個uibutton,並且事件是press。touch up inside,然后,不在碰觸界面(A)。發生了什么?
看看log:

2013-01-06 00:57:21.167 runloop[10146:c07] begin
2013-01-06 00:57:31.171 runloop[10146:c07] end
2013-01-06 00:57:34.173 runloop[10146:3703] timer
2013-01-06 00:58:00.001 runloop[10146:c07] while end
2013-01-06 00:58:00.002 runloop[10146:c07] over


pageStillLoading設置成NO之后過了近30s,while才結束。
如果我把5的mode改成NSRunLoopCommonModes或者在界面上在加一個按鈕,然后不停的點擊那個按鈕(B),結果如下

2013-01-06 01:01:23.944 runloop[10174:c07] begin
2013-01-06 01:01:33.948 runloop[10174:c07] end
2013-01-06 01:01:34.023 runloop[10174:c07] while end
.
.
.
2013-01-06 01:01:36.943 runloop[10174:c07] while end
2013-01-06 01:01:36.950 runloop[10174:370b] timer
2013-01-06 01:01:37.016 runloop[10174:c07] while end
2013-01-06 01:01:37.016 runloop[10174:c07] over

 

為什么?我們看下流程
當2執行的時候,主線程的事情就是運行runloop 10s,而這個runloop本來就在運行的,所以相當於主線程什么都不用做,空閑狀態的主線程當然也可以繼續響應界面上的ui事件哦。
當5執行的時候,也沒有主線程什么事情,runMode:beforeDate:是要么接受到一個mode上的event,要么到date這個時間。所以,A中要等待很久,這句才會返回;而B中,卻是不停的給他event,所以while語句執行了多次。

 

而且B說明,有非default mode的event一直往run loop中發消息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM