NSRunLoop工作原理
通過所有的“消息”都被添加到了NSRunLoop中去,而在這裏這些消息並分爲“input source”和“Timer source” 並在循環中檢查是不是有事件需要發生,如果需要那麼就調用相應的函數處理。
我們在使用NSTimer的時候,可能會接觸到runloop的概念,下面是一個簡單的例子:
1 - (void)viewDidLoad 2 { 3 [super viewDidLoad]; 4 // Do any additional setup after loading the view, typically from a nib. 5 NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 6 target:self 7 selector:@selector(printMessage:) 8 userInfo:nil 9 repeats:YES]; 10 }
這個時候如果我們在界面上滾動一個scrollview,那麼我們會發現在停止滾動前,控制檯不會有任何輸出,就好像scrollView在滾動的時候將timer暫停了一樣,在查看相應文檔後發現,這其實就是runloop的mode在做怪。
NSRunLoopCommonModes默認會包含NSDefaultRunLoopMode和UITrackingRunLoopMode,這個時候如果我們在界面上滾動一個scrollview,那麼我們會發現在停止滾動前,控制檯不會有任何輸出,就好像scrollView在滾動的時候將timer暫停了一樣,在查看相應文檔後發現,這其實就是runloop的mode在做怪。
runloop可以理解爲cocoa下的一種消息循環機制,用來處理各種消息事件,我們在開發的時候並不需要手動去創建一個runloop,因爲框架爲我們創建了一個默認的runloop,通過[NSRunloop currentRunloop]我們可以得到一個當前線程下面對應的runloop對象,不過我們需要注意的是不同的runloop之間消息的通知方式。
接着上面的話題,在開啓一個NSTimer實質上是在當前的runloop中註冊了一個新的事件源,而當scrollView滾動的時候,當前的MainRunLoop是處於UITrackingRunLoopMode的模式下,在這個模式下,是不會處理NSDefaultRunLoopMode的消息(因爲RunLoop Mode不一樣),要想在scrollView滾動的同時也接受其它runloop的消息,我們需要改變兩者之間的runloopmode.
1 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
簡單的說就是NSTimer不會開啓新的進程,只是在Runloop裏註冊了一下,Runloop每次loop時都會檢測這個timer,看是否可以觸發。當Runloop在A mode,而timer註冊在B mode時就無法去檢測這個timer,所以需要把NSTimer也註冊到A mode,這樣就可以被檢測到。