.

Prior to iOS 10, the system’s NSTimer would cause circular references, leading to memory leaks. Here are some solutions to this problem.

After iOS 10, Apple optimized NSTimer to use Block callbacks to solve the circular reference problem.

/ / API_AVAILABLE (macosx (10.12), the ios (10.0), watchos (3.0), tvos (10.0)); [NSTimer scheduledTimerWithTimeInterval: 1.0 repeats: YES block: ^ (NSTimer * _Nonnull timer) {/ / Do some things}].Copy the code

This iOS 10 approach solves the problem of circular references. But most of us still need to adapt to iOS10.

This is how we normally use NSTimer

[NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @ the selector (testTimer) userInfo:nil repeats:YES]; / / or self. MyTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @ the selector (testTimer) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode];

- (void)testTimer {
    NSLog(@"Do Some Things"); } // Stop Timer - (void)dealloc {[self.myTimer invalidate]; self.myTimer = nil; }Copy the code
But did you notice above that the dealloc method is never called, not executed until the program is killed? It’s a big loop. Because self.timer and NSTimer target: self (VC) strongly reference each other and are not released, it is impossible to execute dealloc.

Five ways to solve the NSTimer circular reference problem

Method one:

Use (void) didMoveToParentViewController (UIViewController *) the parent method, in this way to clean off Timer, will release the Timer object, has solved the strong reference. The dealloc method is called.

Note: Not applicable for PresentVC.

/ / life cycle to remove childVC - (void) didMoveToParentViewController: (UIViewController *) parent {if(parent == nil) { [self.myTimer invalidate]; self.myTimer = nil; }}Copy the code

Method 2 middleware method

There is nothing in messaging that middleware can’t solve, and if there is, add middleware

// Define a middleware attribute @property (nonatomic, strong) id target; _target = [NSObject new]; class_addMethod([_target class], @selector(testTimer), (IMP)timerIMP, "v@:"); // use _target instead of self. This did not have the circular reference self. MyTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: _target selector: @ the selector (testTimer) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode];

void timerIMP(id self, SEL _cmd) {
    NSLog(@"Do some things"); } // Stop Timer - (void)dealloc {[self.myTimer invalidate]; self.myTimer = nil; NSLog(@"Timer dealloc");
}
Copy the code

This is also ok.

Method 3: Use the NSProxy class

Create a new class, TimerProxy, that inherits from NSProxy.

Set a property

// Note that you want to use weak @property (nonatomic, weak) id target;Copy the code

Then implement two methods:

Method signature / * * * / - (NSMethodSignature *) methodSignatureForSelector SEL (SEL) {return[self.target methodSignatureForSelector:sel]; Forward} / * * * / - (void) forwardInvocation: (NSInvocation *) invocation {[invocation invokeWithTarget: self. The target]; } where you need it, then import the TimerProxy header file using @Property (nonatomic, strong) TimerProxy * TimerProxy; _timerProxy = [TimerProxy alloc]; // Note that there is only the alloc method _timerproxy. target = self; Self. MyTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: _timerProxy selector: @ the selector (testTimer) userInfo:nil repeats:YES];

Copy the code

This can also solve the circular reference problem.

Method 4: Copy the iOS 10 Block method

Create a new NSTimer class, QLTimer. Define an add method

/ * * to define a method * / + (NSTimer *) QLscheduledTimerWithTimeInterval: NSTimeInterval timeInterval repeats (BOOL) repeats block:(void(^)(void))timerBlock; / / implementation method + (NSTimer *) QLscheduledTimerWithTimeInterval: NSTimeInterval timeInterval repeats: (BOOL) repeats block: nonnull void (^)(void))timerBlock {return[self scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(QLTimerHandle:) userInfo:[timerBlock > > < span style = "padding-bottom: 0px; padding-bottom: 0px; } +(void)QLTimerHandle:(NSTimer *)timer { void(^block)(void) = timer.userInfo;if(block) { block(); }} use __block typeof(self) weakSelf = self; Self. MyTimer = [NSTimer QLscheduledTimerWithTimeInterval: 1.0 repeats: YES block: ^ {__block typeof (weakSelf strongSelf =) weakSelf; [strongSelftestTimer];
    }];
Copy the code

Methods five

It is said to have been officially approved by Apple: Used with GCD Queues.

MSWeakTimer

All of the above methods can solve the NSTimer circular reference problem. So much for the time being, other methods hope to add. Thank you very much!