nstimer 是 ios 上的一種計時器,通過 nstimer 物件,可以指定時間間隔,向乙個物件傳送訊息。nstimer 是比較常用的工具,比如用來定時更新介面,定時傳送請求等等。但是在使用過程中,有很多需要注意的地方,稍微不注意就會產生 bug,crash,記憶體洩漏。本文講解了使用 nstimer 時需要注意的問題。
1. nstimer 容易洩漏
比如以下**建立了乙個計時器:
1
2
3
4
5
6
self.timer =
[nstimer scheduledtimerwithtimeinterval:1
target:self
selector:@selector(update)
userinfo:nil
repeats:yes];
上述**,將建立乙個無限迴圈的 timer,並投入當前執行緒的 runloop 中開始執行。此時,runloop 會引用住 timer,timer 會引用住 self,self 則儲存了 timer。如下圖所示:
需要注意的是,這種無限迴圈的 timer,會一直執行,需要呼叫[timer invalidate]
顯式停止。否則 runloop 會一直引用著 timer,timer 又引用了 self,導致 self 整個物件洩漏,實際情況中,這個 self 有可能是乙個 view,甚至是乙個 controller。
那,[timer invalidate]
要什麼時候呼叫?
有些人會在 self 的 dealloc 裡面呼叫,這幾乎可以確定是錯誤的。因為 timer 會引用住 self,在 timer 停止之前,是不會釋放 self 的,self 的 dealloc 也不可能會被呼叫。
正確的做法應該是根據業務需要,在適當的地方啟動 timer 和 停止 timer。比如 timer 是頁面用來更新頁面內部的 view 的,那可以選擇在頁面顯示的時候啟動 timer,頁面不可見的時候停止 timer。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (
void
- (
void
2. 錯誤特徵
實際開發中,或者 code review 的時候,可以通過一些特徵初步判定可能會有問題。
錯誤特徵 1:
1
2
3
4
- (
void
)dealloc
以上**是有問題的。當 timer 沒有停止的時候,self 會被引用,也就沒有機會走到 dealloc。同時,**作者應該對 timer 沒有正確的認識,所以需要 review 整個 timer 的使用情況。
錯誤特徵 2:
1
2
3
4
5
[nstimer scheduledtimerwithtimeinterval:1
target:self
selector:@selector(update)
userinfo:nil
repeats:yes];
以上**建立了乙個 timer,但是沒有儲存起來,後續自然也沒有機會停止這個 timer。所以會導致 timer 洩漏。
錯誤特徵 3:
1
2
3
4
5
6
7
8
9
10
- (
void
bool
)animated
3. 停止 timer 可能會導致 self 物件銷毀
值得注意的是,呼叫[timer invalidate]
停止 timer,此時 timer 會釋放 target,如果 timer 是最後乙個持有 target 的物件,那麼此次釋放會直接觸發 target 的 。比如:
1
2
3
4
5
- (
void
)onenterbackground:(id)sender
以上**,加入第一行的 invalidate 之後,self 被銷毀了,那麼第二行訪問 self.view 時候,就會觸發野指標 crash。因為 objective-c 的方法裡面,self 是沒有被 retain 的。這種情況,有個臨時的解決方案如下:
1
2
3
4
5
6
- (
void
)onenterbackground:(id)sender
將 self 改為弱引用。但是也是乙個臨時解決方案。正確解決方法是,查出其它物件沒有引用 self 的時候,為什麼 timer 還沒停止。這個案例告訴大家,當見到 invalidate 被呼叫之後很神奇地出現了 self 野指標 crash 的時候,不要驚訝,就是 timer 沒處理好。
4. perform delay
[nsobject performselector:withobject:afterdelay:]
和[nsobject performselector:withobject:afterdelay:inmode:]
我們簡稱為 perform delay,他們的實現原理就是乙個不迴圈(repeat 為 no)的 timer。所以使用這兩個介面的注意事項跟使用 timer 類似。需要在適當的地方呼叫[nsobject cancelpreviousperformrequestswithtarget:selector:object:]
5. runloop mode
注意建立 nstimer 或者呼叫 perform delay 方法,都是往當前執行緒的 runloop 中投遞訊息,大部分介面的預設投遞模式是 cfrunloopdefaultmode。也就是說,runloop 不在 defaultmode 下執行的時候(比如滾動列表的時候主線程的 runloop mode 是 cfrunlooptrackingmode),訊息將被暫時阻塞,不能及時處理。
6. weak timer
nstimer 之所以比較難用對,比較重要的原因主要是 nstimer 對 target 是強引用的。這導致了 target 洩漏,或者生命週期超出開發者的預期。timer 如果對 target 是弱引用的話,這些問題就不存在了,這就是 weak timer。
weak timer 的實現方式分為兩種,第一種是在 nstimer 和 target 中間加多一層**(proxy),**作為 target 被 nstimer 強引用,同時弱引用真正的 target,並對它**訊息。示例圖如下:
NSTimer使用簡單例子
新建乙個工程,往window上新增赤橙黃綠青藍紫七個檢視 做成乙個巢狀的方形,使用nstimer每一秒鐘換一下顏色,要求迴圈換顏色並發生旋轉,待十秒後移除window上所有試圖 剛學,只會用最簡單的方法 首先是建立檢視,我是通過手寫uiview來建立檢視的 self.window uiwindow ...
NStimer 的擴充套件使用
問題 由於nstimer 和 檢視 非常容易迴圈引用 1.這裡提供乙個乙個很好的解決辦法 import inte ce nstimer support nstimer wx scheduledtimerwithtimeinterval nstimeinterval interval block vo...
iOS中NSTimer的使用
我們在開發的過程中,可能會用到計時器,foundation框架中有個類叫做nstimer。我們可以指定絕對的日期與時間,也可以指定執行任務的相對延遲時間,還可以重複執行任務,下面我們來看一下nstimer的使用。計時器要和 執行迴圈 相關聯,執行迴圈到時候會觸發任務。建立nstimer時,可以將其 ...