打破NSTimer的循环引用

1,271 阅读1分钟

NSTimer 的循环引用,出现在 timer 作为ViewController的属性,同时timer中的 target为self

graph TD
ViewController --> timer --> ViewController

破除循环引用,有三种方式。

1、在viewWillDisappear中结束定时器

- (void)viewWillDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
}

这样可以运行到viewcontroller的dealloc,问题是,页面push后再pop,没有定时器工作,所以,可以考虑在viewWillAppear中开启定时器。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (_timer) {
        return;
    }
    _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}

2、封装NSTimer

将timer的内容,放到封装类中,同时在viewcontroller中的dealloc中,释放封装类中的timer

封装类

@interface TestTimer1 : NSObject

- (void)startTimer;

- (void)endTimer;

@end

@interface TestTimer1 ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation TestTimer1

- (void)startTimer {
    _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onTimer) userInfo:nil repeats:YES];
}

- (void)onTimer {
    NSLog(@"timerAction");
}

- (void)endTimer {
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
}

- (void)dealloc {
    NSLog(@"TestTimer1 dealloc");
}

@end

viewcontroller中

- (void)viewDidLoad {
    [super viewDidLoad];
    _timer1 = [TestTimer1 new];
    [_timer1 startTimer];
}

- (void)dealloc {
    [_timer1 endTimer];
    NSLog(@"ViewController2 dealloc");
}

这样,viewcontroller只有强引用TestTimer1。

3、使用NSProxy

NSProxy是一个抽象类,一般用来负责代理消息转发的对象。必须通过子类实例化。

@interface TestTimer2 : NSProxy

- (instancetype)initWithObject:(id)obj;

@end

@interface TestTimer2 ()

@property (nonatomic, weak) id obj;

@end

@implementation TestTimer2

- (instancetype)initWithObject:(id)obj {
    self.obj = obj;
    return self;
}

// 消息转发
- (void)forwardInvocation:(NSInvocation *)invocation {
    if ([self.obj respondsToSelector:invocation.selector]) {
        [invocation invokeWithTarget: self.obj];
    }
}

// 签名注册方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.obj methodSignatureForSelector:sel];
}
@end

在viewcontroller中,记得要在dealloc中释放定时器,否则会出现 "[NSProxy doesNotRecognizeSelector:timerAction]"的崩溃错误。

- (void)viewDidLoad {
    [super viewDidLoad];
    TestTimer2 *timer2 = [[TestTimer2 alloc] initWithObject:self];
    _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:timer2 selector:@selector(timerAction) userInfo:nil repeats:YES];
}

- (void)timerAction {
    NSLog(@"timerAction");
}

- (void)dealloc {
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
    NSLog(@"ViewController2 dealloc");
}

viewcontroller 弱引用 timer2, _timer强引用timer2,timer2弱引用 viewcontroller。