美文网首页
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