         }
         
         @objc func onScreenUpdate() {
-            target?.updateFrame()
+            target?.updateFrameIfNeeded()
         }
     }
 

    }
}

/// Keeps a reference to an `Image` instance and its duration as a GIF frame.
struct AnimatedFrame {
    var image: Image?
    let duration: TimeInterval
    
    static let null = AnimatedFrame(image: .none, duration: 0.0)
}

protocol AnimatorDelegate: AnyObject {
    func animator(_ animator: Animator, didPlayAnimationLoops count: UInt)
}

// MARK: - Animator
class Animator {
    // MARK: Private property
    private let size: CGSize
    private let maxFrameCount: Int
    private let imageSource: CGImageSource
    private let maxRepeatCount: AnimatedImageView.RepeatCount
    
    private var animatedFrames = [AnimatedFrame]()
    private let maxTimeStep: TimeInterval = 1.0
    private var frameCount = 0
    private var currentFrameIndex = 0
    private var currentFrameIndexInBuffer = 0
    private var currentPreloadIndex = 0
    private var timeSinceLastFrameChange: TimeInterval = 0.0
    private var currentRepeatCount: UInt = 0
    
    var needsPrescaling = true
    weak var delegate: AnimatorDelegate?
    
    /// Loop count of animated image.
    private var loopCount = 0
    
    var currentFrame: UIImage? {
        return frame(at: currentFrameIndexInBuffer)
    }

    var isReachMaxRepeatCount: Bool {
        switch maxRepeatCount {
        case .once:
            return currentRepeatCount >= 1
        case .finite(let maxCount):
            return currentRepeatCount >= maxCount
        case .infinite:
            return false
        }
    }
    
    var contentMode = UIView.ContentMode.scaleToFill
    
    private lazy var preloadQueue: DispatchQueue = {
        return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue")
    }()
    
    /// Creates an animator with image source reference.
    ///
    /// - Parameters:
    ///   - source: The reference of animated image.
    ///   - mode: Content mode of the `AnimatedImageView`.
    ///   - size: Size of the `AnimatedImageView`.
    ///   - count: Count of frames needed to be preloaded.
    ///   - repeatCount: The repeat count should this animator uses.
    init(imageSource source: CGImageSource,
         contentMode mode: UIView.ContentMode,
         size: CGSize,
         framePreloadCount count: Int,
         repeatCount: AnimatedImageView.RepeatCount) {
        self.imageSource = source
        self.contentMode = mode
        self.size = size
        self.maxFrameCount = count
        self.maxRepeatCount = repeatCount
    }
    
    func frame(at index: Int) -> Image? {
        return animatedFrames[safe: index]?.image
    }
    
    func prepareFramesAsynchronously() {
        preloadQueue.async { [weak self] in
            self?.prepareFrames()
        }
    }
    
    private func prepareFrames() {
        frameCount = CGImageSourceGetCount(imageSource)
        
        if let properties = CGImageSourceCopyProperties(imageSource, nil),
           let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
           let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int
        {
            self.loopCount = loopCount
        }
        
        let frameToProcess = min(frameCount, maxFrameCount)
        animatedFrames.reserveCapacity(frameToProcess)
        animatedFrames = (0..<frameToProcess).reduce([]) { $0 + pure(prepareFrame(at: $1))}
        currentPreloadIndex = (frameToProcess + 1) % frameCount - 1
    }
    
    private func prepareFrame(at index: Int) -> AnimatedFrame {
        
        guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
            return .null
        }
        
        let defaultGIFFrameDuration = 0.100
        let frameDuration = imageSource.gifProperties(at: index).map {
            gifInfo -> Double in
            
            let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
            let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as Double?
            let duration = unclampedDelayTime ?? delayTime ?? 0.0
            
            /*
             http://opensource.apple.com/source/WebCore/WebCore-7600.1.25/platform/graphics/cg/ImageSourceCG.cpp
             Many annoying ads specify a 0 duration to make an image flash as quickly as
             possible. We follow Safari and Firefox's behavior and use a duration of 100 ms
             for any frames that specify a duration of <= 10 ms.
             See <rdar://problem/7689300> and <http://webkit.org/b/36082> for more information.

             See also: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser.
             */
            return duration > 0.011 ? duration : defaultGIFFrameDuration
        } ?? defaultGIFFrameDuration
        
        let image = Image(cgImage: imageRef)
        let scaledImage: Image?
        
        if needsPrescaling {
            scaledImage = image.kf.resize(to: size, for: contentMode)
        } else {
            scaledImage = image
        }
        
        return AnimatedFrame(image: scaledImage, duration: frameDuration)
    }
    
    /// Updates the current frame if necessary using the frame timer and the duration of each frame in `animatedFrames`.
    func updateCurrentFrame(duration: CFTimeInterval) -> Bool {
        timeSinceLastFrameChange += min(maxTimeStep, duration)
        guard let frameDuration = animatedFrames[safe: currentFrameIndexInBuffer]?.duration, frameDuration <= timeSinceLastFrameChange else {
            return false
        }
        
        timeSinceLastFrameChange -= frameDuration
        
        let lastFrameIndex = currentFrameIndexInBuffer
        currentFrameIndexInBuffer += 1
        currentFrameIndexInBuffer = currentFrameIndexInBuffer % animatedFrames.count
        
        if animatedFrames.count < frameCount {
            preloadFrameAsynchronously(at: lastFrameIndex)
        }
        
        currentFrameIndex += 1
        
        if currentFrameIndex == frameCount {
            currentFrameIndex = 0
            currentRepeatCount += 1

            delegate?.animator(self, didPlayAnimationLoops: currentRepeatCount)
        }

        return true
    }
    
    private func preloadFrameAsynchronously(at index: Int) {
        preloadQueue.async { [weak self] in
            self?.preloadFrame(at: index)
        }
    }
    
    private func preloadFrame(at index: Int) {
        animatedFrames[index] = prepareFrame(at: currentPreloadIndex)
        currentPreloadIndex += 1
        currentPreloadIndex = currentPreloadIndex % frameCount
    }
}

extension CGImageSource {
    func gifProperties(at index: Int) -> [String: Double]? {
        let properties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as Dictionary?
        return properties?[kCGImagePropertyGIFDictionary] as? [String: Double]
    }
}


        return indices ~= index ? self[index] : nil
    }
}
