NSTimer使用注意的問題

總結(jié)一下平時使用NSTimer碰到的幾個小問題:

  • 作為定時器使用時不準(zhǔn)確的問題
  • ScrollView滾動時timer不執(zhí)行的問題
  • 內(nèi)存泄漏的問題

NSTimer準(zhǔn)確性不高的原因

以上幾個問題都跟RunLoop有關(guān)系敢靡,一個 NSTimer 注冊到RunLoop后隙轻,RunLoop 會為其重復(fù)的時間點(diǎn)注冊好事件滚粟。例如 10:00, 10:10, 10:20 這幾個時間點(diǎn)漾橙。RunLoop為了節(jié)省資源斋枢,并不會在非常準(zhǔn)確的時間點(diǎn)回調(diào)這個Timer誊册。Timer 有個屬性叫做 Tolerance (寬容度),標(biāo)示了當(dāng)時間點(diǎn)到后笋敞,容許有多少最大誤差。

如果某個時間點(diǎn)被錯過了挪凑,例如執(zhí)行了一個很長的任務(wù)孕索,則那個時間點(diǎn)的回調(diào)也會跳過去,不會延后執(zhí)行躏碳。就比如等公交搞旭,如果 10:10 時我忙著玩手機(jī)錯過了那個點(diǎn)的公交,那我只能等 10:20 這一趟了菇绵。

ScrollView滾動時timer不執(zhí)行的原因

這里有個概念叫 CommonModes:一個 Mode 可以將自己標(biāo)記為Common屬性(通過將其 ModeName 添加到 RunLoopcommonModes 中)肄渗。每當(dāng) RunLoop 的內(nèi)容發(fā)生變化時,RunLoop 都會自動將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 Common 標(biāo)記的所有Mode里咬最。

應(yīng)用場景舉例:主線程的 RunLoop 里有兩個預(yù)置的 Mode:kCFRunLoopDefaultModeUITrackingRunLoopMode翎嫡。這兩個 Mode 都已經(jīng)被標(biāo)記為Common屬性。DefaultMode 是 App 平時所處的狀態(tài)永乌,TrackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態(tài)惑申。當(dāng)你創(chuàng)建一個 Timer 并加到 DefaultMode 時,Timer 會得到重復(fù)回調(diào)铆遭,但此時滑動一個TableView時硝桩,RunLoop 會將 mode 切換為 TrackingRunLoopMode沿猜,這時 Timer 就不會被回調(diào)枚荣,并且也不會影響到滑動操作。

有時你需要一個 Timer啼肩,在兩個 Mode 中都能得到回調(diào)橄妆,一種辦法就是將這個 Timer 分別加入這兩個 Mode。還有一種方式祈坠,就是將 Timer 加入到頂層的 RunLoop 的 commonModeItems 中害碾。commonModeItems 被 RunLoop 自動更新到所有具有Common屬性的 Mode 里去。

NSTimer導(dǎo)致內(nèi)存泄漏的原因及解決方法

原因
  • NSTimer作為局部變量使用赦拘,在添加到RunLoop中的時會被RunLoop強(qiáng)引用慌随,導(dǎo)致Timer本身不能被釋放。
  • NSTimer作為屬性使用時只能聲明成強(qiáng)引用(使用弱引用添加到RunLoop中會導(dǎo)致奔潰)躺同,在創(chuàng)建timer的過程中阁猜,timer會強(qiáng)引用當(dāng)前添加的target,若target是當(dāng)前timer所屬的對象時蹋艺,會造成循環(huán)引用的問題
  • 當(dāng)timer釋放時所在線程跟timer添加到的RunLoop對應(yīng)的線程不一致時釋放會有問題
解決方法

在合適的時機(jī)調(diào)用invalidate方法剃袍。蘋果官方的描述如下:

This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.

invaliate是唯一一個將timerNSRunLoop對象移除的方法,如果我們是使用targetuserinfo對象創(chuàng)建的捎谨,那么同樣會移除對這些對象強(qiáng)引用民效。

#import "TimerViewController.h"

@interface TimerViewController ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation TimerViewController

- (void)dealloc {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self propertyTimer];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    if ([self.timer isValid]) {
        [self.timer invalidate];
    }
}

#pragma mark - private method

- (void)propertyTimer {
    self.timer = [NSTimer timerWithTimeInterval:5
                                         target:self
                                       selector:@selector(propertyTimerMethod)
                                       userInfo:nil
                                        repeats:YES];
    
    [[NSRunLoop currentRunLoop] addTimer:self.timer
                                 forMode:NSRunLoopCommonModes];
    [self.timer fire];
}

- (void)propertyTimerMethod {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

- (void)localTimer {
    //局部變量 無法釋放
    NSTimer *timer = [NSTimer timerWithTimeInterval:5
                                             target:self
                                           selector:@selector(localTimerMethod)
                                           userInfo:nil
                                            repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer
                                 forMode:NSRunLoopCommonModes];
    [timer fire];
}

- (void)localTimerMethod {
    NSLog(@"%@", NSStringFromSelector(_cmd));
}

參考

深入理解RunLoop
iOS的幾種定時器及區(qū)別

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末憔维,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畏邢,更是在濱河造成了極大的恐慌业扒,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棵红,死亡現(xiàn)場離奇詭異凶赁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逆甜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門虱肄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人交煞,你說我怎么就攤上這事咏窿。” “怎么了素征?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵集嵌,是天一觀的道長。 經(jīng)常有香客問我御毅,道長根欧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任端蛆,我火速辦了婚禮凤粗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘今豆。我一直安慰自己嫌拣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布呆躲。 她就那樣靜靜地躺著异逐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪插掂。 梳的紋絲不亂的頭發(fā)上灰瞻,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音辅甥,去河邊找鬼酝润。 笑死,一個胖子當(dāng)著我的面吹牛肆氓,可吹牛的內(nèi)容都是我干的袍祖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谢揪,長吁一口氣:“原來是場噩夢啊……” “哼蕉陋!你這毒婦竟也來了捐凭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤凳鬓,失蹤者是張志新(化名)和其女友劉穎茁肠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缩举,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垦梆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仅孩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片托猩。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辽慕,靈堂內(nèi)的尸體忽然破棺而出京腥,到底是詐尸還是另有隱情,我是刑警寧澤溅蛉,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布公浪,位于F島的核電站,受9級特大地震影響船侧,放射性物質(zhì)發(fā)生泄漏欠气。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一镜撩、第九天 我趴在偏房一處隱蔽的房頂上張望预柒。 院中可真熱鬧,春花似錦琐鲁、人聲如沸卫旱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至投放,卻和暖如春奈泪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背灸芳。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工涝桅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人烙样。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓冯遂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谒获。 傳聞我的和親對象是個殘疾皇子蛤肌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容