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:
parent
8e18c85b58
commit
0951b525f1
|
@ -105,6 +105,7 @@
|
||||||
84B99C9A1FAE650100ECDEDB /* OPMLRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C991FAE650100ECDEDB /* OPMLRepresentable.swift */; };
|
84B99C9A1FAE650100ECDEDB /* OPMLRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C991FAE650100ECDEDB /* OPMLRepresentable.swift */; };
|
||||||
84B99C9B1FAE650100ECDEDB /* 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 */; };
|
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, ); }; };
|
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 */; };
|
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, ); }; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */,
|
848F6AE81FC2BC50002D422E /* ThreadSafeCache.swift */,
|
||||||
844B5B561FE9D36000C7C76A /* Keyboard.swift */,
|
844B5B561FE9D36000C7C76A /* Keyboard.swift */,
|
||||||
84AD1EA420315A8700BC20B7 /* PasteboardWriterOwner.swift */,
|
84AD1EA420315A8700BC20B7 /* PasteboardWriterOwner.swift */,
|
||||||
|
84C326862038C9F6006A025C /* CoalescingQueue.swift */,
|
||||||
84CFF5241AC3C8A200CEA6C8 /* Foundation */,
|
84CFF5241AC3C8A200CEA6C8 /* Foundation */,
|
||||||
84CFF5551AC3CF4A00CEA6C8 /* AppKit */,
|
84CFF5551AC3CF4A00CEA6C8 /* AppKit */,
|
||||||
84CFF5661AC3D13F00CEA6C8 /* Images */,
|
84CFF5661AC3D13F00CEA6C8 /* Images */,
|
||||||
|
@ -824,6 +827,7 @@
|
||||||
844F91D61D90D86100820C48 /* RSTransparentContainerView.m in Sources */,
|
844F91D61D90D86100820C48 /* RSTransparentContainerView.m in Sources */,
|
||||||
84CFF56E1AC3D20A00CEA6C8 /* NSImage+RSCore.m in Sources */,
|
84CFF56E1AC3D20A00CEA6C8 /* NSImage+RSCore.m in Sources */,
|
||||||
8453F7DF1BDF337800B1C8ED /* RSMacroProcessor.m in Sources */,
|
8453F7DF1BDF337800B1C8ED /* RSMacroProcessor.m in Sources */,
|
||||||
|
84C326872038C9F6006A025C /* CoalescingQueue.swift in Sources */,
|
||||||
842E45CC1ED623C7000A8B52 /* UniqueIdentifier.swift in Sources */,
|
842E45CC1ED623C7000A8B52 /* UniqueIdentifier.swift in Sources */,
|
||||||
84A8358A1D4EC7B80004C598 /* PlistProviderProtocol.swift in Sources */,
|
84A8358A1D4EC7B80004C598 /* PlistProviderProtocol.swift in Sources */,
|
||||||
849A339E1AC90A0A0015BA09 /* NSTableView+RSCore.m in Sources */,
|
849A339E1AC90A0A0015BA09 /* NSTableView+RSCore.m in Sources */,
|
||||||
|
|
|
@ -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, you’ll 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue