Add CoalescingQueue, a FIFO queue of target/selector pairs, used for things like updating table row cells — things that should be coalesced and can be delayed minimally.

This commit is contained in:
Brent Simmons 2018-02-17 15:14:00 -08:00
parent 8e18c85b58
commit 0951b525f1
2 changed files with 99 additions and 0 deletions

View File

@ -105,6 +105,7 @@
84B99C9A1FAE650100ECDEDB /* OPMLRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C991FAE650100ECDEDB /* OPMLRepresentable.swift */; };
84B99C9B1FAE650100ECDEDB /* OPMLRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C991FAE650100ECDEDB /* OPMLRepresentable.swift */; };
84BB45431D6909C700B48537 /* NSMutableDictionary-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB45421D6909C700B48537 /* NSMutableDictionary-Extensions.swift */; };
84C326872038C9F6006A025C /* CoalescingQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C326862038C9F6006A025C /* CoalescingQueue.swift */; };
84C632A0200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C6329E200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
84C632A1200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 84C6329F200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.m */; };
84C632A4200D356E007BEEAA /* SendToBlogEditorApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C632A2200D356E007BEEAA /* SendToBlogEditorApp.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -225,6 +226,7 @@
84B99C931FAE64D400ECDEDB /* DisplayNameProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisplayNameProvider.swift; path = RSCore/DisplayNameProvider.swift; sourceTree = "<group>"; };
84B99C991FAE650100ECDEDB /* OPMLRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OPMLRepresentable.swift; path = RSCore/OPMLRepresentable.swift; sourceTree = "<group>"; };
84BB45421D6909C700B48537 /* NSMutableDictionary-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSMutableDictionary-Extensions.swift"; sourceTree = "<group>"; };
84C326862038C9F6006A025C /* CoalescingQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CoalescingQueue.swift; path = RSCore/CoalescingQueue.swift; sourceTree = "<group>"; };
84C6329E200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSAppleEventDescriptor+RSCore.h"; path = "AppKit/NSAppleEventDescriptor+RSCore.h"; sourceTree = "<group>"; };
84C6329F200D30F1007BEEAA /* NSAppleEventDescriptor+RSCore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSAppleEventDescriptor+RSCore.m"; path = "AppKit/NSAppleEventDescriptor+RSCore.m"; sourceTree = "<group>"; };
84C632A2200D356E007BEEAA /* SendToBlogEditorApp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SendToBlogEditorApp.h; path = AppKit/SendToBlogEditorApp.h; sourceTree = "<group>"; };
@ -369,6 +371,7 @@
848F6AE81FC2BC50002D422E /* ThreadSafeCache.swift */,
844B5B561FE9D36000C7C76A /* Keyboard.swift */,
84AD1EA420315A8700BC20B7 /* PasteboardWriterOwner.swift */,
84C326862038C9F6006A025C /* CoalescingQueue.swift */,
84CFF5241AC3C8A200CEA6C8 /* Foundation */,
84CFF5551AC3CF4A00CEA6C8 /* AppKit */,
84CFF5661AC3D13F00CEA6C8 /* Images */,
@ -824,6 +827,7 @@
844F91D61D90D86100820C48 /* RSTransparentContainerView.m in Sources */,
84CFF56E1AC3D20A00CEA6C8 /* NSImage+RSCore.m in Sources */,
8453F7DF1BDF337800B1C8ED /* RSMacroProcessor.m in Sources */,
84C326872038C9F6006A025C /* CoalescingQueue.swift in Sources */,
842E45CC1ED623C7000A8B52 /* UniqueIdentifier.swift in Sources */,
84A8358A1D4EC7B80004C598 /* PlistProviderProtocol.swift in Sources */,
849A339E1AC90A0A0015BA09 /* NSTableView+RSCore.m in Sources */,

View File

@ -0,0 +1,95 @@
//
// CoalescingQueue.swift
// RSCore
//
// Created by Brent Simmons on 2/17/18.
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
//
import Foundation
// Use when you want to coalesce calls for something like updating visible table cells.
// Calls are uniqued. If you add a call with the same target and selector as a previous call, youll just get one call.
// Targets are weakly-held. If a target goes to nil, the call is not performed.
// The perform date is pushed off every time a call is added.
// Calls are FIFO.
struct QueueCall: Equatable {
weak var target: AnyObject?
let selector: Selector
init(target: AnyObject, selector: Selector) {
self.target = target
self.selector = selector
}
func perform() {
let _ = target?.perform(selector)
}
static func ==(lhs: QueueCall, rhs: QueueCall) -> Bool {
return lhs.target === rhs.target && lhs.selector == rhs.selector
}
}
@objc public final class CoalescingQueue: NSObject {
public let name: String
private let interval: TimeInterval
private var timer: Timer? = nil
private var calls = [QueueCall]()
public init(name: String, interval: TimeInterval = 0.05) {
self.name = name
self.interval = interval
}
public func add(_ target: AnyObject, _ selector: Selector) {
let queueCall = QueueCall(target: target, selector: selector)
add(queueCall)
}
@objc func timerDidFire(_ sender: Any?) {
let callsToMake = calls // Make a copy in case calls are added to the queue while performing calls.
resetCalls()
callsToMake.forEach { $0.perform() }
}
}
private extension CoalescingQueue {
func add(_ call: QueueCall) {
restartTimer()
if !calls.contains(call) {
calls.append(call)
}
}
func resetCalls() {
calls = [QueueCall]()
}
func restartTimer() {
invalidateTimer()
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: false)
}
func invalidateTimer() {
if let timer = timer, timer.isValid {
timer.invalidate()
}
timer = nil
}
}