NSTimer will retain its target object (1)

The timer is associated with the run loop, and running the loop triggers the task. When you create a NSTimer, you can schedule it in the current run loop, or you can create it first, and then the developer will schedule it. In either way, the timer can only trigger the task if it is placed in the running loop, such as creating timers with the following methods and arranging them in the current running loop:

+ (NSTimer *) scheduledTimerWithTimeInterval: (NSTimeInterval) seconds target: (nonnull ID) aTarget selector: (nonnull SEL) aSelector userInfo: (nullable ID) userInfo repeats: (BOOL) yesOrNo

A timer created with this method performs the task after a specified interval of time. It can also be repeated until the developer manually closes it later. The target and selector parameters indicate which method the timer will raise on which object. The timer retains its target object and releases the object until its own expiration. Calling the invalidate method causes the timer to fail and the one-time timer fails after the relevant task has been performed. If the developer sets the timer to repeat execution mode, the invalidate must be called manually to stop it.

Because timers keep their target objects, repeated tasks often result in application “retention ring” problems, such as looking at the following code:

#import < Foundation/Foundation.h> @interface EOCClass: NSObject - (void) startPolling; - - (void) stopPolling; @end
#import "EOCClass.h" @implementation EOCClass {NSTimer *_pollTimer;} - (ID) init [super {return init];} - (void) dealloc {[_pollTimer invalidate];} - (void) stopPolling {[_pollTimer invalidate]; _pollTimer = nil;} - (void) startPolling [NSTimer scheduledTimerWithTimeInterval:5.0 target:self {_pollTimer = selector:@selector (p_doPoll) userInfo:nil repeats:YES];} - (void) p_doPoll something @end {//do}

Just imagine if you created the EOCClass instance and called the startPolling method, because the timer of target is pointing to self, then the instance and the timer reference each other to form a retention ring. To break the retaining ring, you must call the stopPolling method or the system will recover (due to the retention ring system will not exist, the recovery), but we do not guarantee the program can call to perform. When the last external to the EOCClass instance references removed, the case will continue to survive, because timer also holds this example, this causes a memory leak, and the leakage is more serious, because the timer will repeat the timer task, but this situation can not be directly detected by code check.

This problem can be solved by block:

#import < Foundation/Foundation.h> @interface; NSTimer (EOCBlocksSupport) + (NSTimer *) eoc_timerScheduledTimerWithTimeInterval: (NSTimeInterval) interval block: (void) (^) (block) repeats: (BOOL) repeats; @end
#import "NSTimer+EOCBlocksSupport.h" @implementation NSTimer (EOCBlocksSupport) + (NSTimer *) eoc_timerScheduledTimerWithTimeInterval: (NSTimeInterval) interval block: (void) (^) (block) repeats: (BOOL) repeats [self scheduledTimerWithTimeInterval:interval target:self {return selector:@selector (eoc_blockInvoke:) userInfo:[block copy] repeats:repeats];} + (void) eoc_blockInvoke: (NSTimer * timer) {void ((^block)) = timer.userInfo; if (block) {block}} (@end);

The above code will execute the task timer should be packaged into block, the call timer function, the block is passed into the incoming parameters through the copy method will be copied to block on the heap, or later execution to block, the block is invalid. At this time, timer is now the target object is a EOCClass object, the class object without recovery, so the timer need not worry, this program itself can not solve the problem, but provides a solution to the problem, such as modifying the memory leak example above:

- (void) startPolling [NSTimer scheduledTimerWithTimeInterval:5.0 target:self {/ / _pollTimer = selector:@selector (p_doPoll) userInfo:nil repeats:YES] [NSTimer eoc_timerScheduledTimerWithTimeInterval:5.0 block:^{[self; _pollTimer = p_doPoll]; repeats:YES]}};

But since block still holds self, it still leads to the retention ring, but this problem can be solved by changing the self of the block to the weak reference

- (void) startPolling [NSTimer scheduledTimerWithTimeInterval:5.0 target:self {/ / _pollTimer = selector:@selector (p_doPoll) userInfo:nil repeats:YES] __weak EOCClass; *weakSelf = self; _pollTimer = [NSTimer eoc_timerScheduledTimerWithTimeInterval:5.0 block:^{[weakSelf p_doPoll] repeats:YES];}};