NSTimer注意點(diǎn)

參考鏈接:

NSTimer需要注意的地方
這是大神寫(xiě)的整個(gè)思路及解決辦法的文章

一.NSTimer和Run loop Modes

Cocoa中弛饭,每個(gè)線程(NSThread)對(duì)象中內(nèi)部都有一個(gè)run loopNSRunLoop)對(duì)象用來(lái)循環(huán)處理輸入事件(子線程默認(rèn)是沒(méi)創(chuàng)建的只有去獲取Runloop的時(shí)候才創(chuàng)建)擂达。
處理的事件包括兩類,一是來(lái)自Input sources的異步事件,一是來(lái)自Timer sources的同步事件;run Loop在處理輸入事件時(shí)會(huì)產(chǎn)生通知几于,可以通過(guò)Core Foundation向線程中添加run-loop observers來(lái)監(jiān)聽(tīng)特定事件,以在監(jiān)聽(tīng)的事件發(fā)生時(shí)做附加的處理工作话侧。

**Default mode(NSDefaultRunLoopMode)
**
//默認(rèn)模式中幾乎包含了所有輸入源(NSConnection除外),一般情況下應(yīng)使用此模式。
**Connection mode(NSConnectionReplyMode) **
//處理NSConnection對(duì)象相關(guān)事件益老,系統(tǒng)內(nèi)部使用彪蓬,用戶基本不會(huì)使用。
**Modal mode(NSModalPanelRunLoopMode) **
//處理modal panels事件捺萌。
**Event tracking mode(UITrackingRunLoopMode) **
//在拖動(dòng)loop或其他user interface tracking loops時(shí)處于此種模式下档冬,在此模式下會(huì)限制輸入事件的處理。例如桃纯,當(dāng)手指按住UITableView拖動(dòng)時(shí)就會(huì)處于此模式酷誓。
**Common mode(NSRunLoopCommonModes) **
//這是一個(gè)偽模式,其為一組run loop mode的集合态坦,將輸入源加入此模式意味著在Common Modes中包含的所有模式下都可以處理盐数。在Cocoa應(yīng)用程序中,默認(rèn)情況下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法想Common Modes中添加自定義modes伞梯。

**注意: ** 所以 Timer默認(rèn)是添加在 default mode下的玫氢。當(dāng)我們拖動(dòng) UIScrollerView的時(shí)候當(dāng)前的 runloop是在 UITrackingRunLoopMode帚屉。所以無(wú)法執(zhí)行timer的對(duì)應(yīng)方法。

解決方法:
方法一:

在另外的線程中處理定時(shí)器事件琐旁,可把Timer加入到NSOperation中在另一個(gè)線程中調(diào)度;

方法二:

修改Timer運(yùn)行的run loop模式涮阔,將其加入到UITrackingRunLoopMode模式或NSRunLoopCommonModes模式中。

NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(printMessage) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

二.NSTimer的生命周期

NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(printMessage) userInfo:nil repeats:YES];
//Timer 添加到 Runloop 的時(shí)候灰殴,會(huì)被 Runloop 強(qiáng)引用敬特。
//Timer 又會(huì)有一個(gè)對(duì) Target 的強(qiáng)引用。
//所以說(shuō)如果不對(duì)Timer進(jìn)行釋放牺陶,Timer的targer(self)也一直不會(huì)被釋放伟阔。
//有時(shí)候我們我們對(duì)某個(gè)Timer的targer設(shè)置了nil。但沒(méi)設(shè)置[timer invalidate]掰伸。
//其實(shí)這個(gè)對(duì)象還是沒(méi)被釋放的皱炉。timer對(duì)應(yīng)的執(zhí)行方法也一直會(huì)在線程中執(zhí)行。容易造成內(nèi)存泄露狮鸭。

那么問(wèn)題來(lái)了:如果我就是想讓這個(gè) NSTimer 一直輸出合搅,直到 DemoViewController 銷毀了才停止,我該如何讓它停止呢歧蕉?

  • NSTimer 被 Runloop 強(qiáng)引用了灾部,如果要釋放就要調(diào)用 invalidate 方法。
  • 但是我想在 DemoViewController 的 dealloc 里調(diào)用 invalidate 方法惯退,但是 self 被 NSTimer 強(qiáng)引用了赌髓。
  • 所以我還是要釋放 NSTimer 先,然而不調(diào)用 invalidate 方法就不能釋放它催跪。
  • 然而你不進(jìn)入到 dealloc 方法里我又不能調(diào)用 invalidate 方法锁蠕。

** 注意:** NSTimer 在哪個(gè)線程創(chuàng)建就要在哪個(gè)線程停止,否則會(huì)導(dǎo)致資源不能被正確的釋放懊蒸∪偾悖看起來(lái)各種坑還不少。

方法一:
  • weakSelf
    問(wèn)題的關(guān)鍵就在于 self 被 NSTimer 強(qiáng)引用了榛鼎,如果我們能打破這個(gè)強(qiáng)引用問(wèn)題自然而然就解決了逃呼。所以一個(gè)很簡(jiǎn)單的想法就是:weakSelf:
__weak typeof(self) weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:3.0f
                                          target:weakSelf
                                        selector:@selector(timerFire:)
                                        userInfo:nil
                                         repeats:YES];

*然而這并沒(méi)有什么卵用,這里的 __weak__strong唯一的區(qū)別就是:如果在這兩行代碼執(zhí)行的期間self被釋放了者娱, NSTimertarget會(huì)變成nil

  • target
    既然沒(méi)辦法通過(guò)__weakself抽離出來(lái)苏揣,我們可以造個(gè)假的targetNSTimer 黄鳍。這個(gè)假的 target類似于一個(gè)中間的代理人,它做的唯一的工作就是挺身而出接下了 NSTimer的強(qiáng)引用平匈。類聲明如下:*
@interface HWWeakTimerTarget : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer* timer;
@end
@implementation HWWeakTimerTarget
-(void) fire:(NSTimer *)timer {
   if(self.target) {
       [self.target performSelector:self.selector withObject:timer.userInfo];
   } else {
       [self.timer invalidate];
   }
}
@end

然后我們?cè)俜庋b個(gè)假的 scheduledTimerWithTimeInterval 方法

+(NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats {
    HWWeakTimerTarget* timerTarget = [[HWWeakTimerTarget alloc] init];
    timerTarget.target = aTarget;
    timerTarget.selector = aSelector;
    timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                         target:timerTarget
                                                       selector:@selector(fire:)
                                                       userInfo:userInfo
                                                        repeats:repeats];
    return timerTarget.timer;
}

方法二:
  • block
    如果能用 block 來(lái)調(diào)用 NSTimer 那豈不是更好了框沟。我們可以這樣來(lái)實(shí)現(xiàn):
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      block:(HWTimerHandler)block
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats {
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(_timerBlockInvoke:)
                                       userInfo:@[[block copy], userInfo]
                                        repeats:repeats];
}
+(void)_timerBlockInvoke:(NSArray*)userInfo {
    HWTimerHandler block = userInfo[0];
    id info = userInfo[1];
    // or `!block ?: block();` @sunnyxx
    if (block) {
        block(info);
    }
}

這樣我們就可以直接在 block 里寫(xiě)相關(guān)邏輯了:

-(IBAction)fireButtonPressed:(id)sender {
    _timer = [HWWeakTimer scheduledTimerWithTimeInterval:3.0f block:^(id userInfo) {
        NSLog(@"%@", userInfo);
    } userInfo:@"Fire" repeats:YES];
    [_timer fire];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末藏古,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子忍燥,更是在濱河造成了極大的恐慌拧晕,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梅垄,死亡現(xiàn)場(chǎng)離奇詭異厂捞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)队丝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)靡馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人机久,你說(shuō)我怎么就攤上這事臭墨。” “怎么了膘盖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵胧弛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我侠畔,道長(zhǎng)结缚,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任践图,我火速辦了婚禮掺冠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘码党。我一直安慰自己德崭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布揖盘。 她就那樣靜靜地躺著眉厨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兽狭。 梳的紋絲不亂的頭發(fā)上憾股,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音箕慧,去河邊找鬼服球。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颠焦,可吹牛的內(nèi)容都是我干的斩熊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼伐庭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼粉渠!你這毒婦竟也來(lái)了分冈?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤霸株,失蹤者是張志新(化名)和其女友劉穎雕沉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體去件,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坡椒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箫攀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肠牲。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖靴跛,靈堂內(nèi)的尸體忽然破棺而出缀雳,到底是詐尸還是另有隱情,我是刑警寧澤梢睛,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布肥印,位于F島的核電站,受9級(jí)特大地震影響绝葡,放射性物質(zhì)發(fā)生泄漏深碱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一藏畅、第九天 我趴在偏房一處隱蔽的房頂上張望敷硅。 院中可真熱鬧,春花似錦愉阎、人聲如沸绞蹦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)幽七。三九已至,卻和暖如春溅呢,著一層夾襖步出監(jiān)牢的瞬間澡屡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工咐旧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驶鹉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓铣墨,卻偏偏與公主長(zhǎng)得像梁厉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踏兜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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