美文网首页
iOS定时器带来的内存泄漏

iOS定时器带来的内存泄漏

作者: 汉秋 | 来源:发表于2018-07-25 10:49 被阅读202次

    本次代码实践源自一次面试.
    Q:平时使用定时器NSTimer时如何释放应用它的target?
    A:在viewDidDisappear中将调用invalid方法,在将timer置为nil.
    但面试官想要的好像不是这个回答.

    下面是我自己的答案的代码实践

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.timer = [NSTimer scheduledTimerWithTimeInterval:2
                                                      target:self
                                                    selector:@selector(timeAction:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
        [self.timer invalidate];
        self.timer = nil;
    }
    

    上面代码self应用了timer,而timer页应用了self.这就形成了一个互相应用,肯定会有内存泄漏的.

    我在dealloc方法打了断点,当页面消失后,的确会走到dealloc方法,说明这样做是可以释放当前self的.猜测应该是viewDidDisappear中的操作断开了timer对self的应用,从而内存得以释放.

    下面是面试官给的思路

    上面写的代码导致内存释放不了的就是self和timer之间互相应用了,想要self的释放不受timer影响就必须断开二者的互相应用.

    关键点在于开启定时器时的target设置

    我们定义一个中间件作为定时器的target,利用delegate回调定时器事件,由于delegate的弱应用,就实现了timer对self的弱应用.

    //DetailViewController.m
    #import "DetailViewController.h"
    #import "Middleware.h"
    
    @interface DetailViewController ()<MiddlewareDelegate>
    @property (nonatomic, strong) NSTimer *timer;
    @property (nonatomic, strong) Middleware *middleware;
    
    @end
    
    @implementation DetailViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.middleware = [[Middleware alloc] initWithDelegate:self];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:2
                                                      target:self.middleware
                                                    selector:@selector(timeAction:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
    
    - (void)dealloc {
    
    }
    
    - (void)delegateTimerAction {
            NSLog(@"%f",CACurrentMediaTime());
    }
    @end
    
    //Middleware.m
    #import "Middleware.h"
    
    @interface Middleware()
    
    @property (nonatomic, weak) id<MiddlewareDelegate> delegate;
    @end
    
    
    @implementation Middleware
    
    - (instancetype)initWithDelegate:(id<MiddlewareDelegate>)delegate {
        self = [super init];
        if (self) {
            self.delegate = delegate;
        }
        return self;
    }
    
    - (void)timeAction:(NSTimer *)timer {
        
        [self.delegate delegateTimerAction];
    }
    
    @end
    

    当我们推出当前页面时,dealloc方法也会被调用,说明当前类被释放了.

    从iOS10开始,苹果新增了3个创建定时器的方法用于避免循环引用
    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval
                               repeats:(BOOL)repeats
                                 block:(void (^)(NSTimer *timer))block;
    
    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        repeats:(BOOL)repeats
                                          block:(void (^)(NSTimer *timer))block;
    
    - (instancetype)initWithFireDate:(NSDate *)date
                            interval:(NSTimeInterval)interval
                             repeats:(BOOL)repeats
                               block:(void (^)(NSTimer *timer))block;
    

    这三个方法都将定时器要执行的操作放在了block中,并且将当前timer作为block的参数传进去了.

    思考

    对于第一种方法有个问题,如果不是推出当前页面,而push到另一个页面,每次回来都要销毁创建创建销毁.

    对于平时使用的api,只是简单的会用,没有深入去理解内在的机制.以后要多去了解内在机制、原理.

    相关文章

      网友评论

          本文标题:iOS定时器带来的内存泄漏

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