forked from LoopKit/LoopKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWeakSet.swift
More file actions
179 lines (151 loc) · 5.39 KB
/
WeakSet.swift
File metadata and controls
179 lines (151 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//
// WeakSet.swift
// LoopKit
//
// Created by Michael Pangburn
// Copyright © 2019 LoopKit Authors. All rights reserved.
//
import Foundation
public struct Weak<Value> {
// Rather than constrain `Value` to `AnyObject`, we store the value privately as `AnyObject`.
// This allows us to hold weak references to class-constrained protocol types,
// which as types do not themselves conform to `AnyObject`.
private weak var _value: AnyObject?
public var value: Value? {
return _value as? Value
}
public init(_ value: Value) {
// All Swift values are implicitly convertible to `AnyObject`,
// so this runtime check is the tradeoff for supporting class-constrained protocol types.
precondition(Mirror(reflecting: value).displayStyle == .class, "Weak references can only be held of class types.")
_value = value as AnyObject
}
}
/// A set that holds weak references to its members.
///
/// `Element` must be a class or class-constrained protocol type.
public struct WeakSet<Element> {
private var storage: [ObjectIdentifier: Weak<Element>]
public init<S: Sequence>(_ sequence: S) where S.Element == Element {
let keysAndValues = sequence.map { (key: ObjectIdentifier($0 as AnyObject), value: Weak($0)) }
storage = Dictionary(keysAndValues, uniquingKeysWith: { $1 })
}
public mutating func cleanupDeallocatedElements() {
for (id, element) in storage where element.value == nil {
storage.removeValue(forKey: id)
}
}
}
extension WeakSet: SetAlgebra {
public init() {
storage = [:]
}
public var isEmpty: Bool {
return storage.values.allSatisfy { $0.value == nil }
}
public func contains(_ member: Element) -> Bool {
let id = ObjectIdentifier(member as AnyObject)
return storage[id] != nil
}
@discardableResult
public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
let id = ObjectIdentifier(newMember as AnyObject)
if let existingMember = storage[id]?.value {
return (inserted: false, memberAfterInsert: existingMember)
} else {
storage[id] = Weak(newMember)
return (inserted: true, memberAfterInsert: newMember)
}
}
@discardableResult
public mutating func update(with newMember: Element) -> Element? {
let id = ObjectIdentifier(newMember as AnyObject)
let previousMember = storage.removeValue(forKey: id)
storage[id] = Weak(newMember)
return previousMember?.value
}
@discardableResult
public mutating func remove(_ member: Element) -> Element? {
let id = ObjectIdentifier(member as AnyObject)
return storage.removeValue(forKey: id)?.value
}
public mutating func formUnion(_ other: WeakSet<Element>) {
for (id, element) in other.storage where storage[id] == nil {
// Ignore deallocated elements
if element.value != nil {
storage[id] = element
}
}
}
public func union(_ other: WeakSet<Element>) -> WeakSet<Element> {
var result = self
result.formUnion(other)
return result
}
public mutating func formIntersection(_ other: WeakSet<Element>) {
for id in storage.keys where other.storage[id] == nil {
storage.removeValue(forKey: id)
}
}
public func intersection(_ other: WeakSet<Element>) -> WeakSet<Element> {
var result = self
result.formIntersection(other)
return result
}
public mutating func formSymmetricDifference(_ other: WeakSet<Element>) {
for (id, element) in other.storage {
if storage[id] == nil {
// Ignore deallocated elements
if element.value != nil {
storage[id] = element
}
} else {
storage.removeValue(forKey: id)
}
}
}
public func symmetricDifference(_ other: WeakSet<Element>) -> WeakSet<Element> {
var result = self
result.formSymmetricDifference(other)
return result
}
}
extension WeakSet: Sequence {
public struct Iterator: IteratorProtocol {
private var base: Dictionary<ObjectIdentifier, Weak<Element>>.Iterator
fileprivate init(_ base: Dictionary<ObjectIdentifier, Weak<Element>>.Iterator) {
self.base = base
}
public mutating func next() -> Element? {
while let element = base.next()?.value {
if let value = element.value {
return value
}
}
return nil
}
}
public func makeIterator() -> Iterator {
return Iterator(storage.makeIterator())
}
}
extension WeakSet: Equatable {
public static func == (lhs: WeakSet, rhs: WeakSet) -> Bool {
return lhs.identifiers() == rhs.identifiers()
}
private func identifiers() -> Set<ObjectIdentifier> {
let ids = storage.compactMap { (id, element) -> ObjectIdentifier? in
// Ignore deallocated elements
guard element.value != nil else {
return nil
}
return id
}
return Set(ids)
}
}
extension WeakSet: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Element...) {
self.init(elements)
}
}