美文网首页
[定时器]使用NSTimer定时器出现内存泄漏问题

[定时器]使用NSTimer定时器出现内存泄漏问题

作者: Levi段玉磊 | 来源:发表于2018-06-15 00:49 被阅读0次

有没有发现在使用NSTimer类的定时器,实现定时器的类并没有走dealloc方法?这是为什么?

NSTimer常用定时器写法

正常我们利用NSTimer方式实现代码如下:

@property (nonatomic, strong) NSTimer *tanMukuTimer;

- (void)stopTanMuku
{
    [_tanMukuTimer invalidate];
    _tanMukuTimer = nil;
}

- (void)beginTanMuKu
{
    if (_tanMukuTimer == nil) {
        _tanMukuTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(requestNetwork) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:_tanMukuTimer forMode:NSDefaultRunLoopMode];
    }
}

问题

用此方法创建的定时器会在指定的间隔时间执行定时器的方法-requestNetwork,知道开发者稍后将其手动关闭为止,target与selector参数标识计时器将在哪个对象上调用哪个方法,计时器会保留目标对象,等到自身失效,才会释放目标对象,因为计时器是用实例变量来存放的,所以实例也保留了计时器,然而就出现了保留环。除非在某个时间点手动调用-stopTanMuku 方法停止计时器,从而打破保留环,但通过调用方法来避免内存哦泄漏,手动干预的方式并不理想。如果想在系统回收本类时调用停止计时器的方案,打破循环引用,就会出现问题,因为计时器对象有引用计数,所以self实例的保留计数不会为0,因此系统就不会调用dealloc方法,而现在有没有人调用停止计时器的方法,所以就会出现内存泄漏,类永远无法被释放掉。如果是计时器调用的是请求数据方法,那就导致更多其他严重的内存泄漏问题。

解决方法

这种方法无法通过代码检测出来,因此我们要对这个问题进行避免,可以通过block进行解决,因此要写个NSTimer的分类:

@interface NSTimer (WPGBlockSupport)

+ (NSTimer *)wpg_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                          block:(void(^)(void))block
                                        repeats:(BOOL)repeats;

@end

#import "NSTimer+WPGBlockSupport.h"

@implementation NSTimer (WPGBlockSupport)

+ (NSTimer *)wpg_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                          block:(void(^)(void))block
                                        repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(wpg_blockInvoke:) userInfo:[block copy] repeats:repeats];
}

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

@end

将block作为userinfo的参数传进去,只要计时器还有效,就会一直保留着它,传入参数时要通过copy方法将block,copy到堆上,否则等到稍后要执行它的时候,该块可能已经无效了。计时器现在的target是NSTimer的类对象,由于是个单例,所以不用担心产生循环引用的问题。但是这本身还不能解决问题,因为block捕获了self变量,所以block要保留实例,计时器有通过userinfo保留了block,最后实例本身还要保留计时器,不过改成weak引用,就直接打破保留环,打破循环引用。

- (void)upstartQueryTimer
{
    [self upstopQueryTimer];
    if (nil == _upqueryNoticeTimer) {
        __weak typeof(self) weakSelf = self;
        _upqueryNoticeTimer = [NSTimer wpg_scheduledTimerWithTimeInterval:3.0 block:^{
            __strong typeof(self) strongself = weakSelf;
            [strongself startAnimition];
        } repeats:NO];
    }
}

这段代码先定义了弱引用,然后通过weak变量不直接捕获self变量,这样self就不会被计时器所保留,当块开始执行时,而不直接捕获普通的self变量,当开始执行时,立刻在block内生成strong引用,以保证实例执行期间可以存活,但由于是临时变量,strong引用的self并没有存在堆中,执行后自动就被释放了。这样就完美解决了,不会调用系统不会调用dealloc的内存泄漏问题。

思考与行动:

  1. 除了通过NSTimer实现计时器外,还有其他实现定时器的方式么?哪个更好用?
  2. 通过GCD实现的定时器会出现内存泄漏的问题吗?GCD是如何实现定时器方法的?

相关文章

  • 1.定时器相关问题

    问题 1.定时器不准问题2.定时器内存泄漏问题 答案 1.定时器不准问题 1.当我们使用NSTimer/CADis...

  • GCD定时器封装

    前言 iOS开发中,经常需要使用到定时器,使用NSTimer很容易出现内存泄漏,在此简单封装GCD定时器。 1.定...

  • [定时器]使用NSTimer定时器出现内存泄漏问题

    有没有发现在使用NSTimer类的定时器,实现定时器的类并没有走dealloc方法?这是为什么? NSTimer常...

  • 内存管理 01 - NSTimer、CADisplayLink、

    内存管理 01 - NSTimer、CADisplayLink、GCD 定时器 使用 NSTimer、CADisp...

  • 无标题文章

    iOS NSTimer使用详解-开启、关闭、移除 定时器定时器详解ios定时器关闭定时器NSTimer 1、要使用...

  • NSTimer学习笔记

    NSTimer是iOS最常用的定时器工具之一,在使用的时候常常会遇到各种各样的问题,最常见的是内存泄漏,通常我们使...

  • iOS开发(13)内存管理

    一、定时器的内存 1、CADisplayLink、NSTimer使用注意 CADisplayLink、NSTime...

  • 防止内存泄露的NSTimer定时器

    目录 NSTimer的基础用法 NSTimer的内存泄露 安全防侧漏的定时器 NSTimer的基础用法 创建定时器...

  • NSTimer使用分类避免循环引用

    定时器 ,我们开发中用的很多,NSTimer一不小心很容易造成循环引用,引起内存泄漏,于是写了个分类,使用很简...

  • GCD 实现定时器

    NSTimer的定时器方法 About NSTimer GCD中使用dispatch_source_t实现定时器 ...

网友评论

      本文标题:[定时器]使用NSTimer定时器出现内存泄漏问题

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