俗話說的好,前人栽樹后人乘涼,最近看了很多博文,不少博文提到了NSTimer的銷毀問題, 之前我都沒怎么注意,現在對照着文章一一實踐發現坑還真不少。下面是我讀到的幾篇博文分享給大家
@嘯笑天的NSTimer
@龐海礁的個人空間
@汪海的實驗室「iOS 中的 NSTimer」
這幾篇文章闡述了NSTimer的銷毀問題,並且都給出解決方案
這里就提供一個簡潔的方案,是由嘯笑天
提供的
+ (NSTimer *)ez_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)())inBlock repeats:(BOOL)inRepeats { void (^block)() = [inBlock copy]; NSTimer * timer = [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(__executeTimerBlock:) userInfo:block repeats:inRepeats]; return timer; } + (NSTimer *)ez_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)())inBlock repeats:(BOOL)inRepeats { void (^block)() = [inBlock copy]; NSTimer * timer = [self timerWithTimeInterval:inTimeInterval target:self selector:@selector(__executeTimerBlock:) userInfo:block repeats:inRepeats]; return timer; } + (void)__executeTimerBlock:(NSTimer *)inTimer; { if([inTimer userInfo]) { void (^block)() = (void (^)())[inTimer userInfo]; block(); } }
嘯笑天
解決方案的github地址
https://github.com/easyui/EZToolKit/blob/master/EZToolKit/EZCategory/NSTimer%2BEZ_Helper.m
提醒
上面的解決辦法,只是解決了target的強引用問題,但是NSTimer銷毀還是得自己在使用的viewController的dealloc方法中銷毀
- (void)dealloc {
if ([self.timer isValid]) { [self.timer invalidate]; self.timer = nil; } }
或者你有更好的辦法,請拼命的@我
NSTimer的創建方式
創建方式有2種
1.這種創建方式,會主動添加到主循環中,即默認會執行,但當用戶按住其他控件的時候,它就會停止執行,當放開控件,它才繼續執行
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer ez_scheduledTimerWithTimeInterval:1.f block:^{ NSLog(@"nextPage"); [weakSelf nextPage]; } repeats:YES];
2.這種創建方式,不會主動添加到主循環中,得手動添加,有兩種執行模式
NSRunLoopCommonModes 按住其它控件,不會停止執行
NSDefaultRunLoopMode 按住其它控件,會停止執行,和第一種方式一樣
self.timer = [NSTimer ez_timerWithTimeInterval:1.f block:^{ NSLog(@"nextPage"); [weakSelf nextPage]; } repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
ARC工程是可以重寫dealloc方法並被系統調用的,但不需要手動調用父類的dealloc,手寫[super dealloc]方法會報錯,事實上系統會自動幫你調用父類的dealloc方法,不需要你實現。可以通過在dealloc方法中打印log查看控制器是否被釋放。
控制器在被pop后移出棧后會被釋放,但有些時候會發現控制器出棧的時候不會調用dealloc方法,歸根結底,是因為當前控制器被某個對象強引用了,控制器的引用計數不為0,系統無法幫你釋放這部分內存。
控制器中NSTimer沒有被銷毀
當控制器中存在NSTimer時,就需要注意,因為當[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];時,這個/target:self/ 就增加了VC的RetarnCountr, 如果你不將這個timer invalidate,就別想調用dealloc。需要在viewWillDisappear之前需要把控制器用到的NSTimer銷毀。
-
[timer invalidate]; // 銷毀timer
-
timer = nil; // 置nil
控制器中的代理不是weak屬性
例如@property (nonatomic, weak) id<HCAppViewDelegate> delegate;代理要使用弱引用,因為自定義控件是加載在視圖控制器中的,視圖控制器view對自定義控件是強引用,
如果代理屬性設置為strong,則意味着delegate對視圖控制器也進行了強引用,會造成循環引用。導致控制器無法被釋放,最終導致內存泄漏。
控制器中block的循環引用
block會把它里面的所有對象強引用(在ARC下)/PS:MRC下會retain加1/,包括當前控制器self,因此有可能會出現循環引用的問題。
即一個對象有一個Block屬性,然而這個Block屬性中又引用了對象的其他成員變量,那么就會對這個變量本身產生強應用,那么這個對象本身和他自己的Block屬性就形成了循環引用。在ARC下需要修改成這樣:(/也就是生成一個對自身對象的弱引用/)
-
__weak typeof(self) weakSelf = self;
即:保險起見block中所有的涉及到self的全給替換成weakSelf
參考鏈接:
1.https://github.com/piglikeYoung/Study-notes/wiki/NSTimer%E9%94%80%E6%AF%81%E9%97%AE%E9%A2%98
2.https://segmentfault.com/a/1190000003858306
3.http://blog.callmewhy.com/2015/07/06/weak-timer-in-ios/
4.http://www.jvaeyhcd.cc/2016/04/06/iOS%E4%B8%AD%E9%80%A0%E6%88%90dealloc%E4%B8%8D%E8%B0%83%E7%94%A8%E7%9A%84%E5%8E%9F%E5%9B%A0/