iOS之 NSTimer(一)
以前沒怎么了解過這個NSTimer,其實還是有挺多坑的,今天來總結一下:
首先我們一起來看這個:
我在A ?-> (push) -> B控制器,然后再B控制器中開啟了一個NSTimer。然后我又pop到A
?
pop到A的時候,定時器還在運行,并且B沒有被釋放(未調用dealloc)。why?
這就不得不讓我聯想到我上篇寫到的 “常駐線程”了,莫非NSTimer也是添加到了RunLoop?
這說明本例中的NSTimer確實是跑在了NSRunLoop中。
那為什么B沒有釋放呢?
Timer對Target進行了強引用。timer沒有被釋放,那么B就不會被釋放了。也就走不到Dealloc了。那么我們就得在B離開的時候,要對timer進行invalidate。
- (void)viewWillDisappear:(BOOL)animated{[super viewWillDisappear:animated];[_timer invalidate]; }?
?這個時候NSTimer 銷毀了。pop到A的時候,有調用B的dealloc。
?
================ NSTimer 與 RunLoop 的關系
1.什么是NSTimer?
??A timer waits until a certain time interval has elapsed and then fires, sending a specified message to a target object.
timer是一個能從某時刻或者周期性的給target對象發送一條指定的消息。
定時器是線程通知自己做某件事的方法,定時器和你的RunLoop的特定的模式相關。如果定時器所在的模式當前未被RunLoop監視,那么定時器將不會開始 直到RunLoop運行在相應的模式下。如果RunLoop不在運行,那定時器也將永遠不啟動。
?
?
2. NSTimer的生命周期:
You specify whether a timer is repeating or non-repeating at creation time. A non-repeating timer fires once and then invalidates itself automatically, thereby preventing the timer from firing again. By contrast, a repeating timer fires and then reschedules itself on the same run loop.
A repeating timer always schedules itself based on the scheduled firing time, as opposed to the actual firing time
NSTimer 會對外界傳遞的target進行retain。如果是一次性調用(repeats:NO),會在本次調用之后自身invalidate,并且NSTimer retain的那個target會做一次release。
但是,如果是多次重復調用,就需要我們自己手動進行invalidate,不然NSTimer一直存在。
?
invalidate的方法中有這么一段話:
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.NSTimer在那個線程創建就要在那個線程停止,否則資源不能正確的釋放。
?
?
?
3. NSTimer的Tolerance(容差),不是實時機制
無論是單次執行的NSTimer還是重復執行的NSTimer都不是準時的,這與當前NSTimer所處的線程有很大的關系,如果NSTimer當前所處的線程正在進行大數據處理(假設為一個大循環),NSTimer本次執行會等到這個大數據處理完畢之后才會繼續執行。
這期間有可能會錯過很多次NSTimer的循環周期,但是NSTimer并不會將前面錯過的執行次數在后面都執行一遍,而是繼續執行后面的循環,也就是在一個循環周期內只會執行一次循環。
無論循環延遲的多離譜,循環間隔都不會發生變化,在進行完大數據處理之后,有可能會立即執行一次NSTimer循環,但是后面的循環間隔始終和第一次添加循環時的間隔相同。
?
重復工作定時器會基于安排好的時 間而非實際時間調度它自己運行。舉個例子,如果定時器被設定在某一特定時間開始 并 5 秒重復一次,那么定時器會在那個特定時間后 5 秒啟動,即使在那個特定的觸發 時間延遲了。如果定時器被延遲以至于它錯過了一個或多個觸發時間,那么定時器會 在下一個最近的觸發事件啟動,而后面會按照觸發間隔正常執行?
?
4.Timers work in conjunction with run loops
其實上面的例子中我們使用的是“?create the timer and schedule it ?on the current run loop in the default mode”。這個是在主線程中,所以Runloop是開啟的,不需要我們手動打開。
- ? 上面的Source/Timer/Observer被統稱為mode item,一個item可以被同時加入多個mode。但一個item被重復加入同一個mode時是不會有效果的。如果一個mode中一個item都沒有,則RunLoop會被直接退出,不進入循環。
- 一個RunLoop包含若干個Mode,每個Mode又包含活干個Source/Timer/Oberver,每次調用RunLoop的主函數,只能指定其中的一個Mode,?這個Mode被稱作CurrentMode。如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入。這樣做的目的: 為了分隔不同組的Source/Timeer/Oberver,讓其互不影響.
- 在iOS多線程中,每一個線程都有一個Runloop,但是只有主線程的Runloop默認是打開的,其他子線程也就是我們創建的線程的Runloop默認是關閉的,需要我們手動運行。我們可以通過[NSRunLoop currentRunLoop]來獲得當前線程的Runloop,并且調用[runloop addTimer:timer forMode:NSDefaultRunLoopMode]方法將定時器添加到runloop中,最后一定不要忘記調用runloop的run方法將當前runloop開啟,否則NSTimer永遠也不會運行。
?
NSTimer可以創建一個定時源。
NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop]; NSDate *futureDate = [NSDate dateWithTimeIntervalSinceNow:1.0];_timer = [[NSTimer alloc] initWithFireDate:futureDate interval:1 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];[myRunLoop addTimer:_timer forMode:NSDefaultRunLoopMode];?
?5.對于UIScrollView的Timer
當使用NSTimer的scheduledTimerWithTimeInterval方法時。事實上此時Timer會被加入到當前線程的Run Loop中,且模式是默認的NSDefaultRunLoopMode。而如果當前線程就是主線程,也就是UI線程時,某些UI事件,比如UIScrollView的拖動操作,會將Run Loop切換成NSEventTrackingRunLoopMode模式,在這個過程中,默認的NSDefaultRunLoopMode模式中注冊的事件是不會被執行的。也就是說,此時使用scheduledTimerWithTimeInterval添加到RunLoop中的Timer就不會執行。
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];//使用NSRunLoopCommonModes模式,把timer加入到當前Run Loop中。[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/Ohero/p/4828623.html
總結
以上是生活随笔為你收集整理的iOS之 NSTimer(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的is
- 下一篇: Toast的基本用法 吐司打印