Repository: SDWebImage/SDWebImage
File: SDWebImage/Private/SDDisplayLink.m (Objective-C)

Code before:
#endif
}

- (void)stop {
#if SD_MAC
    CVDisplayLinkStop(self.displayLink);
#elif SD_UIKIT
    self.displayLink.paused = YES;
#else
    [self.displayLink invalidate];
#endif
    self.previousFireTime = 0;
    self.nextFireTime = 0;
}

- (void)displayLinkDidRefresh:(id)displayLink {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [_target performSelector:_selector withObject:self];
#pragma clang diagnostic pop
#if SD_UIKIT
    if (kSDDisplayLinkUseTargetTimestamp) {
        self.nextFireTime = self.displayLink.targetTimestamp;
    } else {
        self.previousFireTime = self.displayLink.timestamp;
    }
#endif
#if SD_WATCH
    self.nextFireTime = CFRunLoopTimerGetNextFireDate((__bridge CFRunLoopTimerRef)self.displayLink);
#endif
}

@end

#if SD_MAC
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
    // CVDisplayLink callback is not on main queue
    // Actually `SDWeakProxy` but not `SDDisplayLink`
    SDDisplayLink *object = (__bridge SDDisplayLink *)displayLinkContext;
    if (!object) return kCVReturnSuccess;
    // CVDisplayLink does not use runloop, but we can provide similar behavior for modes
    // May use `default` runloop to avoid extra callback when in `eventTracking` (mouse drag, scroll) or `modalPanel` (modal panel)
    NSString *runloopMode = object.runloopMode;
    if (![runloopMode isEqualToString:NSRunLoopCommonModes] && ![runloopMode isEqualToString:NSRunLoop.mainRunLoop.currentMode]) {
        return kCVReturnSuccess;
    }
    CVTimeStamp outputTime = inOutputTime ? *inOutputTime : *inNow;
    __weak SDDisplayLink *weakObject = object;
    dispatch_async(dispatch_get_main_queue(), ^{
        weakObject.outputTime = outputTime;
        [weakObject displayLinkDidRefresh:(__bridge id)(displayLink)];

Review comment (suggestion):
The first callback (in `SDAnimatedImagePlayer`) will get a temp status about `duration`, because at this time, the previous `targetTimestamp` or `timestamp` is not set. We have some fallback logic

Then the second callback the duration will be accurate enough

Code after:
    } else {
        SDWeakProxy *weakProxy = [SDWeakProxy proxyWithTarget:self];
        self.displayLink = [NSTimer timerWithTimeInterval:kSDDisplayLinkInterval target:weakProxy selector:@selector(displayLinkDidRefresh:) userInfo:nil repeats:YES];
        [self addToRunLoop:self.runloop forMode:self.runloopMode];
    }
#endif
}

- (void)stop {
#if SD_MAC
    CVDisplayLinkStop(self.displayLink);
#elif SD_UIKIT
    self.displayLink.paused = YES;
#else
    [self.displayLink invalidate];
#endif
    self.previousFireTime = 0;
    self.nextFireTime = 0;
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
- (void)displayLinkDidRefresh:(id)displayLink {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [_target performSelector:_selector withObject:self];
#pragma clang diagnostic pop
#if SD_UIKIT
    if (kSDDisplayLinkUseTargetTimestamp) {
        self.nextFireTime = self.displayLink.targetTimestamp;
    } else {
        self.previousFireTime = self.displayLink.timestamp;
    }
#endif
#if SD_WATCH
    self.nextFireTime = CFRunLoopTimerGetNextFireDate((__bridge CFRunLoopTimerRef)self.displayLink);
#endif
}
#pragma clang diagnostic pop

@end

#if SD_MAC
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
    // CVDisplayLink callback is not on main queue
    // Actually `SDWeakProxy` but not `SDDisplayLink`
    SDDisplayLink *object = (__bridge SDDisplayLink *)displayLinkContext;
    if (!object) return kCVReturnSuccess;
    // CVDisplayLink does not use runloop, but we can provide similar behavior for modes
    // May use `default` runloop to avoid extra callback when in `eventTracking` (mouse drag, scroll) or `modalPanel` (modal panel)
    NSString *runloopMode = object.runloopMode;