NSRunloop的簡單認識

最早接觸runloop的概念,是第一次用NSTimer的時候。一個最簡單的例子:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1
                                              target:self
                                            selector:@selector(printMessage:)
                                            userInfo:nil
                                             repeats:YES];
//    [[NSRunLoop currentRunLoop] addTimer:timer
//                                 forMode:NSRunLoopCommonModes];
}

如果我們同時在界面上滾動一個scrollview,那麼我們會發現在滾動停止之前,控制檯是不會有輸出的,就好像scrollView在滾動的時候將timer暫停了一樣。通過了解後發現,其實是Cocoa的RunLoop Mode在作怪。

我把runloop理解爲一種cocoa下的一種消息循環的機制,用來處理各種消息事件。我們在開發的時候一般並不需要手動去創建一個runloop,因爲在程序進入mainThread之後其實就爲我們創建了默認的的mainRunLoop,通過[NSRunloop currentRunLoop]我們就可以得到當前線程對應的RunLoop對象,而我們需要留意的是在多個runloop之間消息的通知方式。

接上面說到的,開啓一個NSTimer實質上是開啓了一個新的線程(Runloop)在當前Runloop中註冊了一個新的事件源,也就是說除了MainRunloop之外還有一個Runloop存在。而當scrollView在滾動的時候,當前MainRunLoop是處於UITrackingRunLoopMode,在該模式下,不會處理 NSDefaultRunLoopMode的消息(因爲Runloop Model不一致),而NSTimer在創建後的RunLoop(B)默認會以NSDefaultRunLoopMode與當前context的Runloop(A)發送消息進行通信。要想在scrollView滾動的同時也接受其他runloop的消息,則需要改變兩者之間的RunLoopMode

 [[NSRunLoop currentRunLoop] addTimer:timer
                              forMode:NSRunLoopCommonModes];

類似的問題在前幾天修改一個http異步通信模塊的時候也碰到了,簡單地說是向服務器異步獲取圖片數據後通知主線程刷新tableView中的圖片,但在tableView滾動還沒有停止或用戶手指還停留在屏幕上的時候,圖片一直不會出來。後來發現請求數據的時候用到了NSURLConnection的

- (id)initWithRequest:(NSURLRequest *)request 
             delegate:(id)delegate;

瞭解後發現該方法創建的異步請求線程和NSTimer一樣,也是NSDefaultRunLoopMode的,與

- (id)initWithRequest:(NSURLRequest *)request 
             delegate:(id)delegate 
     startImmediately:(BOOL)startImmediately

不同的是,上面的方法默認創建後默認直接發起請求,並以NSDefaultRunLoopMode與runloop進行消息傳遞,因此我們需要和NSTimer一樣更改他的RunLoopMode。

NSURLConnection *connection = [[NSURLConnection alloc]
                               initWithRequest:request
                               delegate:self
                               startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop]
            forMode:NSRunLoopCommonModes];
[connection start];
發佈了75 篇原創文章 · 獲贊 6 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章