NSTimer保留对象导致内存泄漏

在做限时支付,验证码发送之类的功能时经常需要使用NTimer来做定时器,但是NSTimer在invalidate之前会保留持有它target对象,导致targtet对象无法释放,即使在delloc中:
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
也无法释放,因为target对象一直被引用就不会进入delloc方法。

  • 所以最基本的方法就是在target对象需要释放之前手动去控制执行计时器的invalidate方法,但有时计时器的invalidate方法并不是完全能控制调用的,因为要确定target对象在最后一个引用释放之前调用计时器的invalidate方法,这通过代码无法完全检测出来。在查看了《Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法》之后得知可以用块来打破“保留环”。

首先,在分类中添加下面这段代码:

#import 

@interface NSTimer (Addtions)

/**
 *  计时器动作
 *
 *  @param interval 时间
 *  @param block    事件
 *  @param repeats  是否重复
 */
+ (NSTimer *)sf_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;


@end
#import "NSTimer+Addtions.h"

@implementation NSTimer(Addtions)

/**
 *  计时器动作
 *
 *  @param interval 时间
 *  @param block    事件
 *  @param repeats  是否重复
 */
+ (NSTimer *)sf_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                    block:(void(^)())block
                                  repeats:(BOOL)repeats{
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(private_blockinvoke:)
                                       userInfo:[block copy]
                                        repeats:repeats];
}


+ (void)private_blockinvoke:(NSTimer *)timer{
    void (^block)() = timer.userInfo;
    if (block) {
        block();
    }
}

@end
  • 在代码中,把要执行的事件封装成“块”,通过userInfo参数传出去(uerInfo可用来存放“不透明值”),那么只要计时器有效,就会一直保留着它。这里有个地方要注意一下,传递block时需要copy一下,把block拷贝到“堆”上,因为定义块的时候,所占的内存区域是分配在栈中的,这说明,块只在定义它的那个范围内有效,通过拷贝就可以把块从栈复制到堆上,块就可以在定义它的范围之外使用。

  • 现在计时器的target是NSTimer类对象,而NSTimer类对象是个单例,是否持有它都无所谓了。


现在我们直接使用这个函数去创建计时器:

    self.timer = [NSTimer sf_scheduledTimerWithTimeInterval:1.0
                                                      block:^{
                                                          [self timerAction];
                                                      } repeats:YES];

这时依然会形成“保留环”,因为block保留了self对象,self又持有timer属性。所以在调用函数之前要使用weak引用来打破它:

    __weak RegistVC *weakSelf = self;
    self.timer = [NSTimer sf_scheduledTimerWithTimeInterval:1.0
                                                      block:^{
                                                          RegistVC *strongSelf = weakSelf;
                                                          [strongSelf timerAction];
                                                      } repeats:YES];

到这里,问题就算解决了,当self的最后一个引用将其释放的时候,self就会被释放了,如果忘记在delloc中调用计时器的invalidate方法,则weakSelf会变为nil。在调试过程中也能看到对象delloc了,而不是再持有一段时间甚至永远不会释放。

你可能感兴趣的:(NSTimer保留对象导致内存泄漏)