CADisplayLink
Start timer
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Copy the code
End timer
[self.displayLink invalidate];
Copy the code
When enabled, the timer is invoked at the same frequency as the screen refreshes, depending on runloop imprecisely.
NSTimer
Start timer
1 / / open the self. The timer = [NSTimer scheduledTimerWithTimeInterval: 1 target: proxy selector: @ the selector (timerTest) the userInfo: nil repeats:YES]; 2 / / open the self. The timer = [NSTimer scheduledTimerWithTimeInterval: 1 repeats: YES block: ^ (NSTimer * _Nonnull timer) {}]. // unwrap 3 self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerTest) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; // unwrap 4 self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {}]; // unwrap 4 self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {}]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];Copy the code
Relying on runloop is inaccurate.
End timer
[self.timer invalidate];
Copy the code
Using scheduledTimerWithTimeInterval created the timer will be added to the current thread runloop NSDefaultRunLoopMode mode
-
CADisplayLink and NSTimer have strong references to target, and circular references are raised if target has strong references to them.
-
The solution
- Using blocks can only be used with timers
__weak typeof(self) weakSelf = self; self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { __strong typeof(self) strongSelf = weakSelf; [strongSelf timeTest]; }]; Copy the code
- Using proxy objects
MyProxy2 *proxy = [MyProxy2 proxyWithTarget:self]; self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:proxy selector:@selector(test) userInfo:nil repeats:YES]; Copy the code
@interface MyProxy : NSObject +(instancetype)proxyWithTarget:(id)target; @property(nonatomic,weak)id target; @end @implementation MyProxy +(instancetype)proxyWithTarget:(id)target{ MyProxy *proxy = [[MyProxy alloc] init]; proxy.target = target; return proxy; } -(id)forwardingTargetForSelector:(SEL)aSelector{ return self.target; } @end Copy the code
@interface MyProxy2 : NSProxy +(instancetype)proxyWithTarget:(id)target; @property(nonatomic,weak)id target; @end @implementation MyProxy2 +(instancetype)proxyWithTarget:(id)target{ MyProxy2 *proxy = [MyProxy2 alloc]; proxy.target = target; return proxy; } - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [self.target methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation{ [invocation invokeWithTarget:self.target]; } @end Copy the code
NSProxy is specially used for forwarding class, more efficient, there is no way to find stage, directly into methodSignatureForSelector forward phase.
The GCD timer
We found that CADisplayLink and NSTimer are not accurate, so we need to use THE GCD to ensure the accuracy of the timer. Start timer
-(void)test{ // dispatch_queue_t queue = dispatch_get_main_queue(); Dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); Dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); Self. timer = timer; self.timer = timer; Dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC); Dispatch_source_set_timer (timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0); // dispatch_source_set_event_handler(timer, ^{NSLog(@"aa")); / /}); // Call dispatch_source_set_event_handler_f(timer,timerFire); // Start timer dispatch_resume(timer); } void timerFire(void *param){NSLog(@"bb, %@",[NSThread currentThread]); }Copy the code
End timer
dispatch_source_cancel(self.timer);
Copy the code