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