Naming & Docs
This commit is contained in:
parent
3cbf95efbf
commit
34cac2aab8
|
@ -25,7 +25,7 @@
|
||||||
OBJ_101 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* XCTestManifests.swift */; };
|
OBJ_101 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* XCTestManifests.swift */; };
|
||||||
OBJ_103 /* Schedule.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Schedule::Schedule::Product" /* Schedule.framework */; };
|
OBJ_103 /* Schedule.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Schedule::Schedule::Product" /* Schedule.framework */; };
|
||||||
OBJ_50 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
|
OBJ_50 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
|
||||||
OBJ_51 /* Cabinet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Cabinet.swift */; };
|
OBJ_51 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Bag.swift */; };
|
||||||
OBJ_52 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* DeinitObserver.swift */; };
|
OBJ_52 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* DeinitObserver.swift */; };
|
||||||
OBJ_53 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Deprecated.swift */; };
|
OBJ_53 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Deprecated.swift */; };
|
||||||
OBJ_54 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Extensions.swift */; };
|
OBJ_54 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Extensions.swift */; };
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
OBJ_74 /* Schedule.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Schedule::Schedule::Product" /* Schedule.framework */; };
|
OBJ_74 /* Schedule.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Schedule::Schedule::Product" /* Schedule.framework */; };
|
||||||
OBJ_81 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
|
OBJ_81 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
|
||||||
OBJ_92 /* AtomicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* AtomicTests.swift */; };
|
OBJ_92 /* AtomicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* AtomicTests.swift */; };
|
||||||
OBJ_93 /* CabinetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* CabinetTests.swift */; };
|
OBJ_93 /* BagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* BagTests.swift */; };
|
||||||
OBJ_94 /* DateTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* DateTimeTests.swift */; };
|
OBJ_94 /* DateTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* DateTimeTests.swift */; };
|
||||||
OBJ_95 /* DeinitObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* DeinitObserverTests.swift */; };
|
OBJ_95 /* DeinitObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* DeinitObserverTests.swift */; };
|
||||||
OBJ_96 /* ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* ExtensionsTests.swift */; };
|
OBJ_96 /* ExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* ExtensionsTests.swift */; };
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
OBJ_10 /* Cabinet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cabinet.swift; sourceTree = "<group>"; };
|
OBJ_10 /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = "<group>"; };
|
||||||
OBJ_11 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
|
OBJ_11 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
|
||||||
OBJ_12 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
|
OBJ_12 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
|
||||||
OBJ_13 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
OBJ_13 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
OBJ_25 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
OBJ_25 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||||
OBJ_26 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
OBJ_26 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
OBJ_29 /* AtomicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicTests.swift; sourceTree = "<group>"; };
|
OBJ_29 /* AtomicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicTests.swift; sourceTree = "<group>"; };
|
||||||
OBJ_30 /* CabinetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CabinetTests.swift; sourceTree = "<group>"; };
|
OBJ_30 /* BagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BagTests.swift; sourceTree = "<group>"; };
|
||||||
OBJ_31 /* DateTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeTests.swift; sourceTree = "<group>"; };
|
OBJ_31 /* DateTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeTests.swift; sourceTree = "<group>"; };
|
||||||
OBJ_32 /* DeinitObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserverTests.swift; sourceTree = "<group>"; };
|
OBJ_32 /* DeinitObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserverTests.swift; sourceTree = "<group>"; };
|
||||||
OBJ_33 /* ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsTests.swift; sourceTree = "<group>"; };
|
OBJ_33 /* ExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionsTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -139,13 +139,24 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
6639192D223FD4B5000F412E /* Utils */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_29 /* AtomicTests.swift */,
|
||||||
|
OBJ_30 /* BagTests.swift */,
|
||||||
|
OBJ_32 /* DeinitObserverTests.swift */,
|
||||||
|
OBJ_33 /* ExtensionsTests.swift */,
|
||||||
|
);
|
||||||
|
name = Utils;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
66C87992223A90D300A95D60 /* Utils */ = {
|
66C87992223A90D300A95D60 /* Utils */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
OBJ_13 /* Extensions.swift */,
|
OBJ_13 /* Extensions.swift */,
|
||||||
OBJ_11 /* DeinitObserver.swift */,
|
OBJ_11 /* DeinitObserver.swift */,
|
||||||
OBJ_9 /* Atomic.swift */,
|
OBJ_9 /* Atomic.swift */,
|
||||||
OBJ_10 /* Cabinet.swift */,
|
OBJ_10 /* Bag.swift */,
|
||||||
);
|
);
|
||||||
name = Utils;
|
name = Utils;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -183,11 +194,8 @@
|
||||||
OBJ_28 /* ScheduleTests */ = {
|
OBJ_28 /* ScheduleTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
OBJ_29 /* AtomicTests.swift */,
|
6639192D223FD4B5000F412E /* Utils */,
|
||||||
OBJ_30 /* CabinetTests.swift */,
|
|
||||||
OBJ_31 /* DateTimeTests.swift */,
|
OBJ_31 /* DateTimeTests.swift */,
|
||||||
OBJ_32 /* DeinitObserverTests.swift */,
|
|
||||||
OBJ_33 /* ExtensionsTests.swift */,
|
|
||||||
OBJ_34 /* Helpers.swift */,
|
OBJ_34 /* Helpers.swift */,
|
||||||
OBJ_35 /* PlanTests.swift */,
|
OBJ_35 /* PlanTests.swift */,
|
||||||
OBJ_36 /* TaskCenterTests.swift */,
|
OBJ_36 /* TaskCenterTests.swift */,
|
||||||
|
@ -358,7 +366,7 @@
|
||||||
buildActionMask = 0;
|
buildActionMask = 0;
|
||||||
files = (
|
files = (
|
||||||
OBJ_50 /* Atomic.swift in Sources */,
|
OBJ_50 /* Atomic.swift in Sources */,
|
||||||
OBJ_51 /* Cabinet.swift in Sources */,
|
OBJ_51 /* Bag.swift in Sources */,
|
||||||
OBJ_52 /* DeinitObserver.swift in Sources */,
|
OBJ_52 /* DeinitObserver.swift in Sources */,
|
||||||
OBJ_53 /* Deprecated.swift in Sources */,
|
OBJ_53 /* Deprecated.swift in Sources */,
|
||||||
OBJ_54 /* Extensions.swift in Sources */,
|
OBJ_54 /* Extensions.swift in Sources */,
|
||||||
|
@ -397,7 +405,7 @@
|
||||||
buildActionMask = 0;
|
buildActionMask = 0;
|
||||||
files = (
|
files = (
|
||||||
OBJ_92 /* AtomicTests.swift in Sources */,
|
OBJ_92 /* AtomicTests.swift in Sources */,
|
||||||
OBJ_93 /* CabinetTests.swift in Sources */,
|
OBJ_93 /* BagTests.swift in Sources */,
|
||||||
OBJ_94 /* DateTimeTests.swift in Sources */,
|
OBJ_94 /* DateTimeTests.swift in Sources */,
|
||||||
OBJ_95 /* DeinitObserverTests.swift in Sources */,
|
OBJ_95 /* DeinitObserverTests.swift in Sources */,
|
||||||
OBJ_96 /* ExtensionsTests.swift in Sources */,
|
OBJ_96 /* ExtensionsTests.swift in Sources */,
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// A value box that can read and write the underlying value atomically.
|
/// Represents a box that can read and write the underlying value atomically.
|
||||||
final class Atomic<T> {
|
final class Atomic<T> {
|
||||||
|
|
||||||
private var v: T
|
private var v: T
|
||||||
private let lock = NSLock()
|
private let lock = NSLock()
|
||||||
|
|
||||||
|
/// Init with the underlying value.
|
||||||
init(_ value: T) {
|
init(_ value: T) {
|
||||||
self.v = value
|
self.v = value
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// A unique key used to remove the corresponding element from a bag.
|
||||||
|
struct BagKey: Equatable {
|
||||||
|
|
||||||
|
fileprivate let i: UInt64
|
||||||
|
|
||||||
|
fileprivate init(underlying: UInt64) {
|
||||||
|
self.i = underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a Boolean value indicating whether two BagKeys are equal.
|
||||||
|
static func == (lhs: BagKey, rhs: BagKey) -> Bool {
|
||||||
|
return lhs.i == rhs.i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A generator can generate a sequence of unique `BagKey`.
|
||||||
|
///
|
||||||
|
/// let k1 = gen.next()
|
||||||
|
/// let k2 = gen.next()
|
||||||
|
/// ...
|
||||||
|
struct BagKeyGenerator: Sequence, IteratorProtocol {
|
||||||
|
|
||||||
|
typealias Element = BagKey
|
||||||
|
|
||||||
|
private var k = BagKey(underlying: 0)
|
||||||
|
|
||||||
|
/// Gets next BagKey.
|
||||||
|
mutating func next() -> Element? {
|
||||||
|
if k.i == UInt64.max {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer { k = BagKey(underlying: k.i + 1) }
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A data structure used to store a sequence of elements.
|
||||||
|
///
|
||||||
|
/// let k1 = bag.append(e1)
|
||||||
|
/// let k2 = bag.append(e2)
|
||||||
|
///
|
||||||
|
/// for e in bag {
|
||||||
|
/// // -> e1
|
||||||
|
/// // -> e2
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// bag.delete(k1)
|
||||||
|
struct Bag<Element> {
|
||||||
|
|
||||||
|
private typealias Entry = (key: BagKey, element: Element)
|
||||||
|
|
||||||
|
private var keys = BagKeyGenerator()
|
||||||
|
private var entries: [Entry] = []
|
||||||
|
|
||||||
|
/// Pushes the given element on to the end of this container.
|
||||||
|
@discardableResult
|
||||||
|
mutating func append(_ new: Element) -> BagKey {
|
||||||
|
let key = keys.next()!
|
||||||
|
|
||||||
|
let entry = (key: key, element: new)
|
||||||
|
entries.append(entry)
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the element for key if key is in this container.
|
||||||
|
func get(_ key: BagKey) -> Element? {
|
||||||
|
if let entry = entries.first(where: { $0.key == key }) {
|
||||||
|
return entry.element
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the element with the given key and returns this element.
|
||||||
|
@discardableResult
|
||||||
|
mutating func delete(_ key: BagKey) -> Element? {
|
||||||
|
if let i = entries.firstIndex(where: { $0.key == key }) {
|
||||||
|
return entries.remove(at: i).element
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all elements from this containers.
|
||||||
|
mutating func clear() {
|
||||||
|
entries.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of elements in this containers.
|
||||||
|
var count: Int {
|
||||||
|
return entries.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Bag: Sequence {
|
||||||
|
|
||||||
|
func makeIterator() -> AnyIterator<Element> {
|
||||||
|
var iterator = entries.makeIterator()
|
||||||
|
return AnyIterator<Element> {
|
||||||
|
return iterator.next()?.element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,70 +0,0 @@
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct CabinetKey: Equatable {
|
|
||||||
|
|
||||||
private let i: UInt64
|
|
||||||
|
|
||||||
init(underlying: UInt64) {
|
|
||||||
self.i = underlying
|
|
||||||
}
|
|
||||||
|
|
||||||
func increased() -> CabinetKey {
|
|
||||||
return CabinetKey(underlying: i &+ 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: CabinetKey, rhs: CabinetKey) -> Bool {
|
|
||||||
return lhs.i == rhs.i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Cabinet<Element> {
|
|
||||||
|
|
||||||
private typealias Entry = (key: CabinetKey, element: Element)
|
|
||||||
|
|
||||||
private var key = CabinetKey(underlying: 0)
|
|
||||||
private var entries: [Entry] = []
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
mutating func append(_ new: Element) -> CabinetKey {
|
|
||||||
defer { key = key.increased() }
|
|
||||||
|
|
||||||
let entry = (key: key, element: new)
|
|
||||||
entries.append(entry)
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(_ key: CabinetKey) -> Element? {
|
|
||||||
if let entry = entries.first(where: { $0.key == key }) {
|
|
||||||
return entry.element
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
mutating func delete(_ key: CabinetKey) -> Element? {
|
|
||||||
if let i = entries.firstIndex(where: { $0.key == key }) {
|
|
||||||
return entries.remove(at: i).element
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func clear() {
|
|
||||||
entries.removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
var count: Int {
|
|
||||||
return entries.count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Cabinet: Sequence {
|
|
||||||
|
|
||||||
func makeIterator() -> AnyIterator<Element> {
|
|
||||||
var iterator = entries.makeIterator()
|
|
||||||
return AnyIterator<Element> {
|
|
||||||
return iterator.next()?.element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,19 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
#if canImport(ObjectiveC)
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
|
||||||
private var deinitObserverKey: Void = ()
|
private var DEINIT_OBSERVER_KEY: Void = ()
|
||||||
|
|
||||||
|
/// Used to observe object deinit.
|
||||||
|
///
|
||||||
|
/// let observer = DeinitObserver.observe(target) {
|
||||||
|
/// print("\(target) deinit")
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// observer.cancel()
|
||||||
class DeinitObserver {
|
class DeinitObserver {
|
||||||
|
|
||||||
private(set) weak var object: AnyObject?
|
private(set) weak var observed: AnyObject?
|
||||||
|
|
||||||
private var action: (() -> Void)?
|
private var action: (() -> Void)?
|
||||||
|
|
||||||
|
@ -14,23 +21,25 @@ class DeinitObserver {
|
||||||
self.action = action
|
self.action = action
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Installs observation.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
static func observe(
|
static func observe(
|
||||||
_ object: AnyObject,
|
_ object: AnyObject,
|
||||||
onDeinit action: @escaping () -> Void
|
onDeinit action: @escaping () -> Void
|
||||||
) -> DeinitObserver {
|
) -> DeinitObserver {
|
||||||
let observer = DeinitObserver(action)
|
let observer = DeinitObserver(action)
|
||||||
observer.object = object
|
observer.observed = object
|
||||||
|
|
||||||
objc_setAssociatedObject(object, &deinitObserverKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
objc_setAssociatedObject(object, &DEINIT_OBSERVER_KEY, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
|
||||||
return observer
|
return observer
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidate() {
|
/// Uninstalls observation.
|
||||||
|
func cancel() {
|
||||||
action = nil
|
action = nil
|
||||||
if let o = object {
|
if let o = observed {
|
||||||
objc_setAssociatedObject(o, &deinitObserverKey, nil, .OBJC_ASSOCIATION_ASSIGN)
|
objc_setAssociatedObject(o, &DEINIT_OBSERVER_KEY, nil, .OBJC_ASSOCIATION_ASSIGN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ extension Int {
|
||||||
|
|
||||||
extension Calendar {
|
extension Calendar {
|
||||||
|
|
||||||
|
/// The gregorian calendar with `en_US_POSIX` locale.
|
||||||
static let gregorian: Calendar = {
|
static let gregorian: Calendar = {
|
||||||
var cal = Calendar(identifier: .gregorian)
|
var cal = Calendar(identifier: .gregorian)
|
||||||
cal.locale = Locale(identifier: "en_US_POSIX")
|
cal.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
@ -31,6 +32,7 @@ extension Calendar {
|
||||||
|
|
||||||
extension Date {
|
extension Date {
|
||||||
|
|
||||||
|
/// Zero o'clock in the morning.
|
||||||
var startOfToday: Date {
|
var startOfToday: Date {
|
||||||
return Calendar.gregorian.startOfDay(for: self)
|
return Calendar.gregorian.startOfDay(for: self)
|
||||||
}
|
}
|
||||||
|
@ -38,10 +40,17 @@ extension Date {
|
||||||
|
|
||||||
extension NSLocking {
|
extension NSLocking {
|
||||||
|
|
||||||
|
/// Executes a closure returning a value while acquiring the lock.
|
||||||
@inline(__always)
|
@inline(__always)
|
||||||
func withLock<T>(_ body: () throws -> T) rethrows -> T {
|
func withLock<T>(_ body: () throws -> T) rethrows -> T {
|
||||||
lock()
|
lock(); defer { unlock() }
|
||||||
defer { unlock() }
|
|
||||||
return try body()
|
return try body()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a closure returning a value while acquiring the lock.
|
||||||
|
@inline(__always)
|
||||||
|
func withLock(_ body: () throws -> Void) rethrows {
|
||||||
|
lock(); defer { unlock() }
|
||||||
|
try body()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,21 @@ import Foundation
|
||||||
/// `ActionKey` represents a token that can be used to remove the action.
|
/// `ActionKey` represents a token that can be used to remove the action.
|
||||||
public struct ActionKey {
|
public struct ActionKey {
|
||||||
|
|
||||||
fileprivate let cabinetKey: CabinetKey
|
fileprivate let bagKey: BagKey
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CabinetKey {
|
extension BagKey {
|
||||||
|
|
||||||
func asActionKey() -> ActionKey {
|
fileprivate func asActionKey() -> ActionKey {
|
||||||
return ActionKey(cabinetKey: self)
|
return ActionKey(bagKey: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Task` represents a timed task.
|
/// `Task` represents a timed task.
|
||||||
open class Task {
|
open class Task {
|
||||||
|
|
||||||
|
public let id = UUID()
|
||||||
|
|
||||||
public typealias Action = (Task) -> Void
|
public typealias Action = (Task) -> Void
|
||||||
|
|
||||||
private let _mutex = NSRecursiveLock()
|
private let _mutex = NSRecursiveLock()
|
||||||
|
@ -23,8 +25,8 @@ open class Task {
|
||||||
private var _iterator: AnyIterator<Interval>
|
private var _iterator: AnyIterator<Interval>
|
||||||
private var _timer: DispatchSourceTimer
|
private var _timer: DispatchSourceTimer
|
||||||
|
|
||||||
private lazy var _onElapseActions = Cabinet<Action>()
|
private lazy var _onElapseActions = Bag<Action>()
|
||||||
private lazy var _onDeinitActions = Cabinet<Action>()
|
private lazy var _onDeinitActions = Bag<Action>()
|
||||||
|
|
||||||
private lazy var _suspensions: UInt64 = 0
|
private lazy var _suspensions: UInt64 = 0
|
||||||
private lazy var _timeline = Timeline()
|
private lazy var _timeline = Timeline()
|
||||||
|
@ -34,8 +36,8 @@ open class Task {
|
||||||
private lazy var _lifetime: Interval = Int.max.seconds
|
private lazy var _lifetime: Interval = Int.max.seconds
|
||||||
private lazy var _lifetimeTimer: DispatchSourceTimer = {
|
private lazy var _lifetimeTimer: DispatchSourceTimer = {
|
||||||
let timer = DispatchSource.makeTimerSource()
|
let timer = DispatchSource.makeTimerSource()
|
||||||
timer.setEventHandler {
|
timer.setEventHandler { [weak self] in
|
||||||
self.cancel()
|
self?.cancel()
|
||||||
}
|
}
|
||||||
timer.schedule(after: _lifetime)
|
timer.schedule(after: _lifetime)
|
||||||
timer.resume()
|
timer.resume()
|
||||||
|
@ -100,7 +102,7 @@ open class Task {
|
||||||
|
|
||||||
/// Execute this task now, without disrupting its plan.
|
/// Execute this task now, without disrupting its plan.
|
||||||
public func execute() {
|
public func execute() {
|
||||||
let actions = _mutex.withLock { () -> Cabinet<Task.Action> in
|
let actions = _mutex.withLock { () -> Bag<Task.Action> in
|
||||||
let now = Date()
|
let now = Date()
|
||||||
if _timeline.firstExecution == nil {
|
if _timeline.firstExecution == nil {
|
||||||
_timeline.firstExecution = now
|
_timeline.firstExecution = now
|
||||||
|
@ -117,7 +119,7 @@ open class Task {
|
||||||
execute()
|
execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if canImport(ObjectiveC)
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||||
open func host(on target: AnyObject) {
|
open func host(on target: AnyObject) {
|
||||||
DeinitObserver.observe(target) { [weak self] in
|
DeinitObserver.observe(target) { [weak self] in
|
||||||
self?.cancel()
|
self?.cancel()
|
||||||
|
@ -287,7 +289,7 @@ open class Task {
|
||||||
/// Removes action by key from this task.
|
/// Removes action by key from this task.
|
||||||
public func removeAction(byKey key: ActionKey) {
|
public func removeAction(byKey key: ActionKey) {
|
||||||
_mutex.withLock {
|
_mutex.withLock {
|
||||||
_ = _onElapseActions.delete(key.cabinetKey)
|
_ = _onElapseActions.delete(key.bagKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,9 +308,8 @@ open class Task {
|
||||||
|
|
||||||
extension Task: Hashable {
|
extension Task: Hashable {
|
||||||
|
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(ObjectIdentifier(self))
|
hasher.combine(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a boolean value indicating whether two tasks are equal.
|
/// Returns a boolean value indicating whether two tasks are equal.
|
||||||
|
|
|
@ -22,13 +22,13 @@ extension Timeline: CustomStringConvertible {
|
||||||
|
|
||||||
/// A textual representation of this timeline.
|
/// A textual representation of this timeline.
|
||||||
public var description: String {
|
public var description: String {
|
||||||
enum Cache {
|
enum Lazy {
|
||||||
static let fmt = ISO8601DateFormatter()
|
static let fmt = ISO8601DateFormatter()
|
||||||
}
|
}
|
||||||
|
|
||||||
let desc = { (d: Date?) -> String in
|
let desc = { (d: Date?) -> String in
|
||||||
guard let d = d else { return "nil" }
|
guard let d = d else { return "nil" }
|
||||||
return Cache.fmt.string(from: d)
|
return Lazy.fmt.string(from: d)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Timeline: { " +
|
return "Timeline: { " +
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import Schedule
|
@testable import Schedule
|
||||||
|
|
||||||
final class CabinetTests: XCTestCase {
|
final class BagTests: XCTestCase {
|
||||||
|
|
||||||
typealias Fn = () -> Int
|
typealias Fn = () -> Int
|
||||||
|
|
||||||
func testCabinetKey() {
|
func testBagKey() {
|
||||||
let key = CabinetKey(underlying: 0)
|
var g = BagKeyGenerator()
|
||||||
XCTAssertEqual(key.increased(), CabinetKey(underlying: 1))
|
let k1 = g.next()
|
||||||
|
let k2 = g.next()
|
||||||
|
XCTAssertNotNil(k1)
|
||||||
|
XCTAssertNotNil(k2)
|
||||||
|
XCTAssertNotEqual(k1, k2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAppend() {
|
func testAppend() {
|
||||||
var cabinet = Cabinet<Fn>()
|
var cabinet = Bag<Fn>()
|
||||||
let k1 = cabinet.append { 1 }
|
cabinet.append { 1 }
|
||||||
let k2 = cabinet.append { 2 }
|
cabinet.append { 2 }
|
||||||
|
|
||||||
XCTAssertEqual(k1.increased(), k2)
|
|
||||||
XCTAssertEqual(cabinet.count, 2)
|
XCTAssertEqual(cabinet.count, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGet() {
|
func testGet() {
|
||||||
var cabinet = Cabinet<Fn>()
|
var cabinet = Bag<Fn>()
|
||||||
let k1 = cabinet.append { 1 }
|
let k1 = cabinet.append { 1 }
|
||||||
let k2 = cabinet.append { 2 }
|
let k2 = cabinet.append { 2 }
|
||||||
|
|
||||||
|
@ -33,12 +36,10 @@ final class CabinetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertEqual(fn1(), 1)
|
XCTAssertEqual(fn1(), 1)
|
||||||
XCTAssertEqual(fn2(), 2)
|
XCTAssertEqual(fn2(), 2)
|
||||||
|
|
||||||
XCTAssertNil(cabinet.get(k2.increased()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDelete() {
|
func testDelete() {
|
||||||
var cabinet = Cabinet<Fn>()
|
var cabinet = Bag<Fn>()
|
||||||
|
|
||||||
let k1 = cabinet.append { 1 }
|
let k1 = cabinet.append { 1 }
|
||||||
let k2 = cabinet.append { 2 }
|
let k2 = cabinet.append { 2 }
|
||||||
|
@ -52,12 +53,10 @@ final class CabinetTests: XCTestCase {
|
||||||
XCTAssertNotNil(fn2)
|
XCTAssertNotNil(fn2)
|
||||||
|
|
||||||
XCTAssertEqual(cabinet.count, 0)
|
XCTAssertEqual(cabinet.count, 0)
|
||||||
|
|
||||||
XCTAssertNil(cabinet.delete(k2.increased()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClear() {
|
func testClear() {
|
||||||
var cabinet = Cabinet<Fn>()
|
var cabinet = Bag<Fn>()
|
||||||
|
|
||||||
cabinet.append { 1 }
|
cabinet.append { 1 }
|
||||||
cabinet.append { 2 }
|
cabinet.append { 2 }
|
||||||
|
@ -69,7 +68,7 @@ final class CabinetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSequence() {
|
func testSequence() {
|
||||||
var cabinet = Cabinet<Fn>()
|
var cabinet = Bag<Fn>()
|
||||||
cabinet.append { 0 }
|
cabinet.append { 0 }
|
||||||
cabinet.append { 1 }
|
cabinet.append { 1 }
|
||||||
cabinet.append { 2 }
|
cabinet.append { 2 }
|
||||||
|
@ -82,7 +81,7 @@ final class CabinetTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
static var allTests = [
|
static var allTests = [
|
||||||
("testCabinetKey", testCabinetKey),
|
("testBagKey", testBagKey),
|
||||||
("testAppend", testAppend),
|
("testAppend", testAppend),
|
||||||
("testGet", testGet),
|
("testGet", testGet),
|
||||||
("testDelete", testDelete),
|
("testDelete", testDelete),
|
|
@ -1,7 +1,7 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
@testable import Schedule
|
@testable import Schedule
|
||||||
|
|
||||||
#if canImport(ObjectiveC)
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
|
||||||
final class DeinitObserverTests: XCTestCase {
|
final class DeinitObserverTests: XCTestCase {
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ final class DeinitObserverTests: XCTestCase {
|
||||||
let observer = DeinitObserver.observe(obj) {
|
let observer = DeinitObserver.observe(obj) {
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
observer.invalidate()
|
observer.cancel()
|
||||||
}
|
}
|
||||||
fn()
|
fn()
|
||||||
XCTAssertEqual(i, 1)
|
XCTAssertEqual(i, 1)
|
||||||
|
|
|
@ -8,7 +8,7 @@ public func allTests() -> [XCTestCaseEntry] {
|
||||||
testCase(TaskCenterTests.allTests),
|
testCase(TaskCenterTests.allTests),
|
||||||
testCase(TaskTests.allTests),
|
testCase(TaskTests.allTests),
|
||||||
testCase(AtomicTests.allTests),
|
testCase(AtomicTests.allTests),
|
||||||
testCase(CabinetTests.allTests),
|
testCase(BagTests.allTests),
|
||||||
testCase(CalendarTests.allTests),
|
testCase(CalendarTests.allTests),
|
||||||
testCase(ExtensionsTests.allTests)
|
testCase(ExtensionsTests.allTests)
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue