NSTimer使用不当就会造成内存泄漏,比如常见的使用方法:
//定义
@property (nonatomic, strong) NSTimer *timer;
//实现
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(showMsg) userInfo:nil repeats:YES];
//销毁
-(void)dealloc
{
[self.timer invalidate];
self.timer = nil;
}
由于NSTimer会引用住self,而 self又持有NSTimer对象,所以形成循环引用,dealloc 永远不会被执行,timer 也永远不会被释放,造成内存泄漏。
尝试解决办法:
1、把timer改成弱引用
@property (nonatomic, weak) NSTimer *timer;
虽然self对timer是弱引用,但是控制的delloc方法的执行依赖于timer的invalidate,timer的invalidate又依赖于控制器的delloc方法,依旧是循环引用问题;
2、使用__weak
那换个思路能不能让NSTimer弱引用target:
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(showMsg) userInfo:nil repeats:YES];
weak关键字适用于block,当block引用了块外的变量时,会根据修饰变量的关键字来决定是强引用还是弱引用,如果变量使用weak关键字修饰,那block会对变量进行弱引用,如果没有__weak关键字,那就是强引用。 但是NSTimer的 scheduledTimerWithTimeInterval:target方法内部不会判断修饰target的关键字,所以这里传self 和 weakSelf是没区别的,其内部会对target进行强引用,还是会产生循环引用。
最终解决办法
1、使用苹果提供带block的api
2、类似于工程重写一个定时器(HWWeakTimer),用一个含有weak属性的对象A包裹self作为target,再对A进行消息转发,访问A就相当于访问self,这样就完美的解决了循环引用,且保留了target-action方式。
HWWeakTimer最核心代码:
@interface HWWeakTimerTarget : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer* timer;
@end
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats
inMode:(NSRunLoopMode)mode {
HWWeakTimerTarget* timerTarget = [[HWWeakTimerTarget alloc] init];
timerTarget.target = aTarget;
timerTarget.selector = aSelector;
HWWeakTimerParam *param = [[HWWeakTimerParam alloc] init];
param.userInfo = userInfo;
param.runLoopMode = mode;
NSTimer *timer = [NSTimer timerWithTimeInterval:interval
target:timerTarget
selector:@selector(fire:)
userInfo:param
repeats:repeats];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:mode];
timerTarget.timer = timer;
return timerTarget.timer;
}