// // DisplayLinkService.swift // GraphTest // // Created by Andrei Salavei on 4/7/19. // Copyright © 2019 Andrei Salavei. All rights reserved. // import Foundation #if os(macOS) import Cocoa #else import UIKit #endif import CoreGraphics public protocol DisplayLinkListner: AnyObject { func update(delta: TimeInterval) } // DispatchSource mares refreshes more accurate class DisplayLinkService { let listners = NSHashTable.weakObjects() static let shared = DisplayLinkService() public func add(listner: DisplayLinkListner) { listners.add(listner) startDisplayLink() } public func remove(listner: DisplayLinkListner) { listners.remove(listner) if listners.count == 0 { stopDisplayLink() } } private init() { dispatchSourceTimer.schedule(deadline: .now() + 1.0 / 60, repeating: 1.0 / 60) dispatchSourceTimer.setEventHandler { DispatchQueue.main.sync { self.fire() } } } private var dispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: .global(qos: .userInteractive)) private var dispatchSourceTimerStarted: Bool = false private var previousTickTime = 0.0 private func startDisplayLink() { guard !dispatchSourceTimerStarted else { return } dispatchSourceTimerStarted = true previousTickTime = CACurrentMediaTime() dispatchSourceTimer.resume() } private func stopDisplayLink() { guard dispatchSourceTimerStarted else { return } dispatchSourceTimerStarted = false dispatchSourceTimer.suspend() } public func fire() { let currentTime = CACurrentMediaTime() let delta = currentTime - previousTickTime previousTickTime = currentTime let allListners = listners.allObjects var hasListners = false for listner in allListners { (listner as! DisplayLinkListner).update(delta: delta) hasListners = true } if !hasListners { stopDisplayLink() } } }