[iOS]解決NSTimer造成的內(nèi)存泄露

NSTimer佛玄,沒錯(cuò)硼一,定時(shí)器。我們開發(fā)中經(jīng)常使用到的一個(gè)東西梦抢,而且我們?cè)谑褂盟臅r(shí)候差不多都是按照以下代碼來使用的:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

}

- (void)timerAction:(NSTimer *)timer
{
    NSLog(@"%@", timer);
}

but般贼,如果你將timer所屬的控制器推出后,發(fā)現(xiàn)timer此時(shí)還在執(zhí)行奥吩,這是因?yàn)閠imer的執(zhí)行需要依賴于runloop哼蛆,在timer創(chuàng)建好放入runloop之后,并且如果timer是循環(huán)執(zhí)行的圈驼,如果不顯示調(diào)用invalidate方法人芽,那么timer是停不下來的。
同時(shí)绩脆,如果此時(shí)你重寫了這個(gè)控制器的dealloc方法萤厅,并且讓這個(gè)控制器pop出navigation所管理的棧時(shí)(我的這個(gè)控制器是由navigation所push出來的)橄抹,你會(huì)發(fā)現(xiàn)dealloc方法并不會(huì)執(zhí)行,這表明控制器并沒有被釋放惕味,這是為什么呢楼誓,這是因?yàn)镹STimer在添加target時(shí),會(huì)對(duì)這個(gè)target進(jìn)行retain名挥。所以就會(huì)造成上面這種情況:控制器要釋放疟羹,就要釋放它的所有實(shí)例變量,當(dāng)釋放到timer時(shí)禀倔,timer要釋放他所持有的target榄融,而此時(shí)的target是該控制器,所以造成了循環(huán)引用救湖,從而造成了內(nèi)存泄露愧杯。
要避免這種情況,我能想到的一個(gè)辦法就是在設(shè)定timer的target時(shí)鞋既,將target-action保存力九,target改設(shè)置為另一個(gè)和timer不存在引用關(guān)系的變量,進(jìn)而避免泄露邑闺。
代碼如下:
首先定義一個(gè)用來保存target-action的對(duì)象

@interface PltTimerTarget : NSObject

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;

@end

@implementation PltTimerTarget

- (void)pltTimerTargetAction:(NSTimer *)timer
{
    if (self.target) {
        //該方法會(huì)在RunLoop為DefaultMode時(shí)才會(huì)調(diào)用跌前,與timer的CommonMode沖突
        //[self.target performSelector:self.selector withObject:timer afterDelay:0.0];
      
        //該方法可以正常在CommonMode中調(diào)用,但是會(huì)報(bào)警告
        //[self.target performSelector:self.selector withObject:timer];

        //最終方法
        IMP imp = [self.target methodForSelector:self.selector];
        void (*func)(id, SEL, NSTimer*) = (void *)imp;
        func(self.target, self.selector, timer);
    } else {
        [self.timer invalidate];
        self.timer = nil;
    }
}

@end

然后我們自己定義一個(gè)方法陡舅,用來設(shè)置timer(這里我定義的是默認(rèn)循環(huán)的timer抵乓,這種比不循環(huán)的要常用)

+ (instancetype)pltScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo
{
    PltTimerTarget *timerTarget = [[PltTimerTarget alloc] init];
    timerTarget.target = aTarget;
    timerTarget.selector = aSelector;
    NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(pltTimerTargetAction:) userInfo:userInfo repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    timerTarget.timer = timer;
    return timerTarget.timer;
}

這樣,我們就能在代碼中正常使用timer了

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.timer = [NSTimer pltScheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction:) userInfo:@"userInfo"];
    
    //這樣創(chuàng)建的timer蹭沛,target的dealloc方法不會(huì)執(zhí)行臂寝,因?yàn)閠imer會(huì)持有target章鲤,進(jìn)而造成循環(huán)引用
//    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction:) userInfo:@"userInfo" repeats:YES];
    
}

- (void)timerAction:(NSTimer *)timer
{
    NSLog(@"%@", timer.userInfo);
}

- (void)dealloc
{
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"%@ dealloc", self);
}

此時(shí)摊灭,dealloc方法是會(huì)執(zhí)行的,并且能順利的將timer從runloop中停止败徊,避免了內(nèi)存泄露和資源浪費(fèi)帚呼。
Demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市皱蹦,隨后出現(xiàn)的幾起案子煤杀,更是在濱河造成了極大的恐慌,老刑警劉巖沪哺,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沈自,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辜妓,警方通過查閱死者的電腦和手機(jī)枯途,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門忌怎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酪夷,你說我怎么就攤上這事榴啸。” “怎么了晚岭?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵鸥印,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我坦报,道長(zhǎng)库说,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任片择,我火速辦了婚禮璃弄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘构回。我一直安慰自己夏块,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布纤掸。 她就那樣靜靜地躺著脐供,像睡著了一般。 火紅的嫁衣襯著肌膚如雪借跪。 梳的紋絲不亂的頭發(fā)上政己,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音掏愁,去河邊找鬼歇由。 笑死,一個(gè)胖子當(dāng)著我的面吹牛果港,可吹牛的內(nèi)容都是我干的沦泌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼辛掠,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谢谦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萝衩,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤回挽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后猩谊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體千劈,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年牌捷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了墙牌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袁梗。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖憔古,靈堂內(nèi)的尸體忽然破棺而出遮怜,到底是詐尸還是另有隱情,我是刑警寧澤鸿市,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布锯梁,位于F島的核電站,受9級(jí)特大地震影響焰情,放射性物質(zhì)發(fā)生泄漏陌凳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一内舟、第九天 我趴在偏房一處隱蔽的房頂上張望合敦。 院中可真熱鬧,春花似錦验游、人聲如沸充岛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽崔梗。三九已至,卻和暖如春垒在,著一層夾襖步出監(jiān)牢的瞬間蒜魄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工场躯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谈为,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓踢关,卻偏偏與公主長(zhǎng)得像伞鲫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子耘成,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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