Object-c Programming tips-timer
Object-c Timer
The object-c timer automatically releads the current user. If you do not pay attention to calling invalidate, it will easily lead to memory leakage caused by cyclic reference. The following provides a feasible solution.
Example:
You may need to automatically refresh the page in viewController. After data acquisition fails, the system automatically refreshes the data every 10 seconds to obtain the data again. It is very convenient to use NSTimer at this time. Generally, create a repeat object of NSTimer and implement the corresponding timerFireMethod method. When the user actively clicks the return button, the interface should be released, but the interface memory is leaked due to NSTimer retain's current viewController. You may say that invalidate is called in dealloc, but you must understand that dealloc will not be called at all. Of course, viewDidDisappear will not be called.
I read objective object-c some time ago and learned a good idea.
Add a category to NSTimer and use block to pass timerFireMethod. The Code is as follows:
@implementation NSTimer(LPBLocks)+(NSTimer*) lpScheduleTimerWithTimerInternal:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{ return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(lpTimerBlockInvoke:) userInfo:[block copy] repeats:repeats];}+(void)lpTimerBlockInvoke:(NSTimer*)timer{ void(^block)() = timer.userInfo; if(block){ block(); }}@end
This scheduledTimer method also retain target, but because it is a class method, it retains class objects, so there will be no problem. It is passed into the block to be executed, and then the block is obtained through userInfo In the callback function and executed.
Improvement:
This is a great improvement. We can safely pass block code into the code. However, if viewController members are introduced in the block and timer is used as a member variable in viewController.
For example, the following code:
@interface LPNextViewController (){ NSTimer* refreshTimer;}
In this way, viewController and refreshTimer are in the logical circle of circular reference. Of course, weak_self can be used in the block to avoid circular references, but it is always difficult to write code, and it must be explicitly performed by external users.
It is easy to think that it should be encapsulated into a dedicated LPTimer class. It is responsible for holding nstmer and the block of nstmer uses the weak version of LPTimer.
@interface LPTimer (){ NSTimer* _pollTimer; //timer selector __weak id _weak_target; SEL _selector; id _userInfo;}@end
-(Void) scheduleTimerWithTimerInternal :( NSTimeInterval) interval target :( id) target selector :( SEL) aSelector userInfo :( id) userInfo repeats :( BOOL) repeats {_ weak id weak_self = self; _ weak_target = target; _ selector = aSelector; _ userInfo = userInfo; // borrow the block idea of the first version. // call the aSelector method of _ weak_target by using the Layer 2 indirect method. // In this way, stopTimer can be encapsulated, and the stop of timer does not need to be managed externally. _ PollTimer = [NSTimer lpScheduleTimerWithTimerInternal: 1 block: ^ {[weak_self doTimer];} repeats: repeats];}
The above code LPTimer holds the NSTimer object, while the block executed by nstmer uses weak_self. It calls its own doTimer method when timer is triggered. The doTimer is responsible for passing methods to external users.
-(void)doTimer{ if ([_weak_target respondsToSelector:_selector]) { [_weak_target performSelector:_selector withObject:self]; } else{ DLog(@"WARNNING: unknown selector"); }}
_ Weak_target is an external user. External users can think of LPTimer as a common object, and there is no problem with holding it. LPTimer retains a weak reference pointing to an external user. When the time is triggered by timer, it is first transferred to the NStimer block, then passed to the doTimer of LPTimer, and then called to the selector of _ weak_target.
You must release the nstmer object and call the invalidate method of nstmer when the LPTimer is released.
-(void)stopTimer{ DLog(@""); [_pollTimer invalidate];}-(void)dealloc{ [self stopTimer]; DLog(@"");}
In fact, the LPTimer class is used by the user. Therefore, the LPTimer should behave exactly like NSTimer, and the combined adapter mode can be easily used.
Summary:
The basic idea is that NSTimer will retain an object, and now let it retain class object. When it comes to trigger, the nstmer class object is triggered to the Block, and then triggered to the external LPTimer common object. In normal objects, we can process them freely. Use weak_target to make LPTimer weak reference to external users and disconnect external users from LPTimer. Use weak_self to disconnect the cycle association between LPTimer and nstmer. I personally think it is a good idea. If you need it, you are welcome to discuss it.