NSRunLoop的簡單介紹

NSRunLoop核心內容很多,這里僅結合自己實際開發遇到的情況,參考相關博客,給出自己的一點理解。主要是NSTimer和線程

一、NSRunLoop是什么鬼?

NSRunLoop是iOS中的消息處理機制。一般情況下,一個線程執行完成之后就會退出,比如說一條NSLog打印語句,但是有時候我們需要一種機制,執行完某個事件后線程不退出,而是進入休眠狀態,當再次監測到需要出發事件時,線程激活,處理事件,處理完成后再次進入休眠。NSRunLoop就是這樣一種機制。像Button的touch事件,UIImageView的gesture事件均是NSRunLoop在起作用。

二、為什么我沒有覺察到NSRunLoop的存在

默認情況下,我們并不需要手動創建一個RunLoop(我們也確實這么做的,似乎程序也能正常運行),那是因為cocoa框架為我們創建了一個默認的RunLoop,所以Button的點擊事件才能夠正常執行。

三、NSTimer

一般情況下,APP首頁都會有一個廣告輪播的效果圖,然后還會加上定時器,每隔幾秒切換圖片。

NSTimer *timer = [NSTimerscheduledTimerWithTimeInterval:5.0f target:selfselector:@selector(timeAction) userInfo:nil repeats:YES];

但是我們發現,當我們下拉刷新或者滾動scrollView(tableView和collection一樣)時,定時器不工作了。為什么呢?

1.正如上面所說,程序啟動時,系統已經在主線程中默認加入了RunLoop,這保證了我們的主線程在運行起來后處于一種“等待”的狀態。而主線程的RunLoop有兩個Mode:kCFRunLoopDefaultMode(APP平時所處的狀態,幾乎包括所有的輸入源)和UITrackingRunLoopMode(追蹤模式,當滑動,拖拽時進入的模式)

2.創建一個Timer,默認是加入到RunLoop的DefaultMode,不滑動scrollView時,因為App默認也是在DefaultMode,所以定時器正常工作;但是,當你滑動scrollView的時候,RunLoop會將mode切換為UITrackingRunLoopMode,這時候Timer就不會被回調。

自己理解:RunLoop就是一個大的循環圈,會對這個大的循環圈內的相同Mode的事件進行檢測,當監聽到事件需要處理時,跳轉到事件進行處理。每次運行一個run loop,你指定(顯式或隱式)run loop的運行模式。當相應的模式傳遞給run loop時,只有與該模式對應的input sources才被監控并允許run loop對事件進行處理(與此類似,也只有與該模式對應的observers才會被通知)

在開啟一個NSTimer實質上是在當前的runloop中注冊了一個新的事件源,而當scrollView滾動的時候,當前的MainRunLoop是處于UITrackingRunLoopMode的模式下,在這個模式下,是不會處理NSDefaultRunLoopMode的消息(因為RunLoop Mode不一樣),要想在scrollView滾動的同時也接受其它runloop的消息,我們需要改變兩者之間的runloopmode.

簡單的說就是NSTimer不會開啟新的進程,只是在Runloop里注冊了一下,Runloop每次loop時都會檢測這個timer,看是否可以觸發。當Runloop在A mode,而timer注冊在B mode時就無法去檢測這個timer,所以需要把NSTimer也注冊到A mode,這樣就可以被檢測到。

這里有個概念叫 "CommonModes":一個 Mode 可以將自己標記為"Common"屬性(通過將其 ModeName 添加到 RunLoop 的 "commonModes" 中)。每當 RunLoop 的內容發生變化時,RunLoop 都會自動將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 標記的所有Mode里。

應用場景舉例:主線程的 RunLoop 里有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。這兩個 Mode 都已經被標記為"Common"屬性。DefaultMode 是 App 平時所處的狀態,TrackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。當你創建一個 Timer 并加到 DefaultMode 時,Timer 會得到重復回調,但此時滑動一個TableView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,并且也不會影響到滑動操作。

有時你需要一個 Timer,在兩個 Mode 中都能得到回調,一種辦法就是將這個 Timer 分別加入這兩個 Mode。還有一種方式,就是將 Timer 加入到頂層的 RunLoop 的 "commonModeItems" 中。"commonModeItems" 被 RunLoop 自動更新到所有具有"Common"屬性的 Mode 里去。

[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];//加入這句即可

NSRunLoopCommonModes 這是一組可配置的通用模式。將input sources與該模式關聯則同時也將input sources與該組中的其它模式進行了關聯。

這里使用的模式是:NSRunLoopCommonModes,這個模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的結合。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容