04.Timer优化

前言

NSTimer 是 iOS 开发中老生常谈的知识点了,今天再来唠叨一番。

一、 NSTimer 常规使用

通过下面简单的 Demo 看看现象。

#import "NSTimerViewController.h"

@interface NSTimerViewController ()
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) NSInteger count;
@end

@implementation NSTimerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self selfTargetTimer];
}

#pragma mark - 不同 NSTimer 使用方式
/// 常规使用 self 作为 Target 的 NSTimer
- (void)selfTargetTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countAction) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

#pragma mark - Action
- (void)countAction {
    self.count += 1;
    NSLog(@"%@ - %ld",self, self.count);
}

- (void)dealloc {
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"NSTimerViewController dealloc");
}

@end

我们反复 Push 到这个 ViewController ,出现了典型的内存泄露。

1

通过内存图我们能够发现,根本原因是 RunLoop -> Timer <-> Self 这样的一个持有关系。

2

而在该API的文档中也是有说明的。

3

为了解决内存泄露,我们需要打破这个循环引用。

1.1 weakSelf

我们用 weakself 替代 Self 是否有用呢?

4

还是一样,这里是 RunLoop -> Timer <-> weakSelf -> Self 依旧没有打破。

1.2 block

还有这样一个 API

我们试试

5

这个 API 就可以很好的避免循环引用了。这里的关系是 RunLoop -> Timer -> block 。这里只需要注意不要让 block 产生内存泄露就ok了。

二、 自定义 Timer

2.1 加一层间接

通过封装,解决 TimerSelf 的强引用关系: RunLoop -> Timer -> ?? ~~ Self

6

2.2 消息转发机制

我们同样可以利用消息转发机制来对 Timer 进行优化,这里的思想和上面一样,都是使用了一层间接,但是具体实现方式不同。这里使用了 NSProxy 结合消息转发实现。

虽然 Target 并不是 Self 但经过消息转发,最终处理消息的还是消息的还是 Self

7

Last updated

Was this helpful?