NSTimer & CADisplayLink & GCD of timers in iOS

Keywords: Mobile iOS


In daily development, we often need to deal with timing, such as the cycle of some animation, SMS countdown, etc. this note summarizes some of the development I used in my work, please communicate more


The most basic timer in iOS is implemented by Runloop in essence. Generally, it is more accurate, but when there are more time-consuming operations in the running cycle, it will be inaccurate. At the same time, it is also affected by the Mode of the added Runloop. For details, please refer to the features of Runloop


The construction method is mainly divided into automatic start and manual start. The manual start construction method requires us to start the NSTimer after it is created, and the block construction method is added after IOS 10 to prevent memory leakage and like

///Construct and turn on (starting NSTimer is essentially adding it to the RunLoop)
// Those prefixed with "scheduledTimer" are those that automatically start NSTimer, such as:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

///Construct but not open
// The prefix of "timer" is construction only and not enabled, such as:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

///Manual start construction method after iOS 10
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
///Self starting construction method after iOS 10
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

It needs to be explained here that the essence of Timer is to add it to the Timer list of running cycle. If the current cycle is added and there is no special accident, the next running cycle will trigger it and then carry out a series of operations. Therefore, in addition to the construction, NSTimer needs to add Runloop.


When the timer is released, it must be pointed in the middle before it can be destroyed.

- (void)invalidate;

Immediate execution

After setting the timer delay, sometimes it needs to be executed immediately. You can use the fire method:

- (void)fire;


CADisplayLink is based on the refresh cycle of the screen. Compared with NSTimer, it is more accurate. The refresh frequency of iOS screen is 60 times / second, so the time of one time is 1/60s. If your interval time is greater than this time, you are recommended to modify it, otherwise it will cause delay. Its essence is also through the Runloop, so it is not hard to see that when the Runloop chooses other modes or is operated too much by time consuming, it will still cause delay.

// Create CADisplayLink
CADisplayLink *disLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkMethod)];
// Add to RunLoop
[disLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// Termination timer
[disLink invalidate];
// Destroy object
disLink = nil;

In daily development, the proper use of CADisplayLink has an optimization effect. For example, for the progress bar of dynamic calculation progress, as long as its progress feedback is for UI update, when the calculation progress frequency exceeds the frame number, it will cause a lot of meaningless calculation. If the method of calculation progress is bound to CADisplayLink to call, it will only calculate the progress at each screen refresh, and optimize the performance. MBProcessHUB uses this feature.


GCD timer is dispatch_ source_ Type T variable, which can achieve more accurate timing effect, as follows

/** Create timer object
 * para1: DISPATCH_SOURCE_TYPE_TIMER Is timer type
 * para2-3: The middle two parameters are useless for the timer
 * para4: Finally, for which scheduling queue to use
_gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
/** set timer 
 * para2: Task start time
 * para3: Interval between tasks
 * para4: Acceptable error time, setting 0 means no error is allowed
 * Tips: In nanoseconds
dispatch_source_set_timer(_gcdTimer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
/** Set timer task
 * You can use block mode
 * You can also use C function
dispatch_source_set_event_handler(_gcdTimer, ^{
    static int gcdIdx = 0;
    NSLog(@"GCD Method: %d", gcdIdx++);
    NSLog(@"%@", [NSThread currentThread]);
    if(gcdIdx == 5) {
        // Termination timer
// Start task, GCD timer needs to be started manually after creation

GCD more accurate reason

By observing the above demo, we can find that the GCD timer actually uses the dispatch source, which listens to the system kernel object and processes it. Dispatch is similar to the producer consumption mode. By listening to the system kernel object, the producer automatically notifies the corresponding dispatch queue to execute after generating data, and the latter acts as the consumer. More accurate through system call.

The problem of timer inaccuracy and its solution

Through the above description, we can roughly understand the reasons for the inaccuracy. In summary, the main reasons are

  • The current Runloop is too busy
  • Runloop mode is different from timer mode

Knowing the reason, it's easy to give a solution:

  • Avoid too much time-consuming operation concurrency
  • Using GCD timer
  • Create a new thread and start Runloop, add timer to it and set the mode of timer joining to NSRunLoopCommonModes

Timer memory leak

When using the timer, special attention should be paid to memory management. In common cases, the timer object cannot be released, resulting in memory leakage. In serious cases, the controller cannot be released. The related problems are as follows:

NSTimer failed to release


  • Memory modifier use week
  • The construction method uses customization or iOS 10 followed by block construction. In essence, it is not strongly held by both parties

Posted by riceje7 on Mon, 22 Jun 2020 01:50:31 -0700