The loop reference to NSTimer, which appears in the timer as a property of the ViewController, and the target in the timer is self

graph TD
ViewController --> timer --> ViewController

There are three ways to break circular references.

1. End the timer in viewWillDisappear

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

So that’s going to run the dealloc in the ViewController, but the problem is, the page pushes and pops, and there’s no timer working, so think about turning on the timer in viewWillAppear.

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

2. Package NSTimer

Put the contents of the timer in the wrapper class, and release the timer in the wrapper class in dealloc in the ViewController

Encapsulating class @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"); } @endCopy the code

In the viewcontroller

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

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

Thus, the ViewController only has strong references to TestTimer1.

3. Use NSProxy

NSProxy is an abstract class that is generally used as the object responsible for proxy message forwarding. Must be instantiated by subclass.

@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; } / / forward - (void) forwardInvocation invocation: (NSInvocation *) {if ([self. The obj respondsToSelector: invocation. The selector]) { [invocation invokeWithTarget: self.obj]; }} / sign/registration method - (NSMethodSignature *) methodSignatureForSelector: (SEL) SEL {return [self. Obj methodSignatureForSelector:sel]; } @endCopy the code

In the viewcontroller, remember to release in the dealloc timer, otherwise there will be a “[NSProxy doesNotRecognizeSelector: timerAction]” the collapse of the error.

- (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");
}
Copy the code

The viewController weakly references Timer2, _timer strongly references timer2, and Timer2 weakly references the ViewController.