美文网首页
NSTimer保留对象导致内存泄漏

NSTimer保留对象导致内存泄漏

作者: funnyS | 来源:发表于2016-06-04 01:50 被阅读464次

    在做限时支付,验证码发送之类的功能时经常需要使用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 <Foundation/Foundation.h>
    
    @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保留对象导致内存泄漏

          本文链接:https://www.haomeiwen.com/subject/xmjldttx.html