NSTimer 销毁问题 和 iOS中控制器的释放问题

俗话说的好,前人栽树后人乘凉,最近看了很多博文,不少博文提到了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/

上一篇:iOS中数组遍历的方法及比较


下一篇:iOS中数组遍历的方法及比較