Just a Computer Graphics Studio & My Life

[iOS] NSRunLoop和NSTimer

製作一個好的App有非常多因素,而其中使用者體驗非常重要,影響用戶是否會繼續使用我們的App。

原本以為Timer會固定時間就觸發,然而在多線程的狀況下並不是如此,因為每個事件都會被排程,假如前面執行事件會花長時間,就會影響原本我們預定時機點想觸發的Timer。

目前遇到的狀況:

手指滑動TableView,原本每秒執行的Timer卻不運作,直到放開手指。

原來把Timer加入到RunLoop就能解決問題!

程式碼這麼寫:

    timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];

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

果真它就如預期運行了!

接下來由高手解釋什麼是NSRunLoop

NSRunLoop是消息機制的處理模式

NSRunLoop的作用在於有事情做的時候使的當前NSRunLoop的線程工作,沒有事情做讓當前NSRunLoop的線程休眠。

NSTimer默認添加到當前NSRunLoop中,也可以手動制定添加到自己新建的NSRunLoop。

NSRunLoop就是一直在循環檢測,從線程start到線程end,檢測inputsource(如點擊,雙擊等操作)同步事件,檢測timesource同步事件,檢測到輸入源會執行處理函數,首先會產生通知,corefunction向線程添加runloop observers來監聽事件,意在監聽事件發生時來做處理。

在單線程的app中,不需要注意Run Loop,但不代表沒有。程序啟動時,系統已經在主線程中加入了Run Loop。它保證了我們的主線程在運行起來後,就處於一種「等待」的狀態(而不像一些命令行程序一樣運行一次就結束了),這個時候如果有接收到的事件(Timer的定時到了或是其他線程的消息),就會執行任務,否則就處於休眠狀態。

runloopmode是一個集合,包括監聽:事件源,定時器,以及需通知的runloop observers。

模式包括:

  • default模式:幾乎包括所有輸入源(除NSConnection) NSDefaultRunLoopMode模式
  • mode模式:處理modal panels
  • connection模式:處理NSConnection事件,屬於系統內部,用戶基本不用
  • event tracking模式:如組件拖動輸入源UITrackingRunLoopModes 不處理定時事件
  • common modes模式:NSRunLoopCommonModes 這是一組可配置的通用模式。將input sources與該模式關聯則同時也將input sources與該組中的其它模式進行了關聯。

每次運行一個run loop,你指定(顯式或隱式)run loop的運行模式。當相應的模式傳遞給run loop時,只有與該模式對應的input sources才被監控並允許run loop對事件進行處理(與此類似,也只有與該模式對應的observers才會被通知)

例:

  1. 在timer與table同時執行情況,當拖動table時,runloop進入UITrackingRunLoopModes模式下,不會處理定時事件,此時timer不能處理,所以此時將timer加入到NSRunLoopCommonModes模式(addTimer forMode)。
  2. 在scroll一個頁面時來鬆開,此時connection不會收到消息,由於scroll時runloop為UITrackingRunLoopModes模式,不接收輸入源,此時要修改connection的mode。

參考:關於NSRunLoop和NSTimer的深入理解

發表留言

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

你正使用 WordPress.com 帳號留言。 登出 / 變更 )

Twitter picture

你正使用 Twitter 帳號留言。 登出 / 變更 )

Facebook照片

你正使用 Facebook 帳號留言。 登出 / 變更 )

Google+ photo

你正使用 Google+ 帳號留言。 登出 / 變更 )

連結到 %s

標籤雲

%d 位部落客按了讚: