Skip to content
This repository was archived by the owner on Jun 19, 2022. It is now read-only.

Commit c3a9256

Browse files
committed
Back WeakDictionary with NSMapTable
1 parent 59a371d commit c3a9256

File tree

1 file changed

+92
-29
lines changed

1 file changed

+92
-29
lines changed

WeakDictionary/WeakDictionary.swift

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,90 +8,153 @@
88

99
import Foundation
1010

11+
fileprivate class AnyHashableBox: NSObject {
12+
private let hashable: AnyHashable
13+
14+
init<T: Hashable>(_ base: T) {
15+
hashable = AnyHashable(base)
16+
}
17+
18+
var base: Any {
19+
return hashable.base
20+
}
21+
22+
@objc override var hash: Int {
23+
return hashable.hashValue
24+
}
25+
26+
@objc override func isEqual(_ object: Any?) -> Bool {
27+
return hashable == (object as! AnyHashableBox).hashable
28+
}
29+
}
30+
1131
public struct WeakDictionary<Key: Hashable, Value: AnyObject> {
1232

13-
private var storage: [Key: WeakDictionaryReference<Value>]
33+
private var storage: NSMapTable<AnyHashableBox, Value>
1434

1535
public init() {
16-
self.init(storage: [Key: WeakDictionaryReference<Value>]())
36+
self.init(storage: NSMapTable(keyOptions: .strongMemory, valueOptions: .weakMemory))
1737
}
1838

1939
public init(dictionary: [Key: Value]) {
20-
var newStorage = [Key: WeakDictionaryReference<Value>]()
21-
dictionary.forEach({ key, value in newStorage[key] = WeakDictionaryReference<Value>(value: value) })
40+
let newStorage = NSMapTable<AnyHashableBox, Value>(keyOptions: .strongMemory, valueOptions: .weakMemory, capacity: dictionary.capacity)
41+
dictionary.forEach({ key, value in newStorage.setObject(value, forKey: AnyHashableBox(key)) })
2242
self.init(storage: newStorage)
2343
}
2444

25-
private init(storage: [Key: WeakDictionaryReference<Value>]) {
45+
private init(storage: NSMapTable<AnyHashableBox, Value>) {
2646
self.storage = storage
2747
}
2848

29-
public mutating func reap() {
30-
storage = weakDictionary().storage
31-
}
32-
3349
public func weakDictionary() -> WeakDictionary<Key, Value> {
3450
return self[startIndex ..< endIndex]
3551
}
3652

3753
public func dictionary() -> [Key: Value] {
3854
var newStorage = [Key: Value]()
3955

40-
storage.forEach { key, value in
41-
if let retainedValue = value.value {
42-
newStorage[key] = retainedValue
56+
for key in storage.keyEnumerator() {
57+
if let value = storage.object(forKey: (key as! AnyHashableBox)) {
58+
newStorage[(key as! AnyHashableBox).base as! Key] = value
4359
}
4460
}
4561

4662
return newStorage
4763
}
4864
}
4965

66+
public class WeakDictionaryIndex<Key: Hashable, Value> {
67+
fileprivate let key: AnyHashableBox?
68+
69+
private let position: UInt
70+
private var enumerator: NSEnumerator?
71+
private var nextIndex: WeakDictionaryIndex<Key, Value>?
72+
73+
fileprivate init(enumerator: NSEnumerator?, position: UInt) {
74+
self.key = enumerator?.nextObject() as! AnyHashableBox?
75+
self.enumerator = enumerator
76+
self.position = position
77+
}
78+
79+
fileprivate init() {
80+
self.key = nil
81+
self.enumerator = nil
82+
self.position = UInt.max
83+
}
84+
85+
fileprivate func next() -> WeakDictionaryIndex<Key, Value> {
86+
if let nextIndex = self.nextIndex {
87+
return nextIndex
88+
}
89+
90+
let nextIndex = WeakDictionaryIndex<Key, Value>(enumerator: enumerator, position: position + 1)
91+
92+
self.nextIndex = nextIndex
93+
self.enumerator = nil
94+
95+
return nextIndex
96+
}
97+
}
98+
99+
extension WeakDictionaryIndex: Equatable {
100+
public static func == (lhs: WeakDictionaryIndex<Key, Value>, rhs: WeakDictionaryIndex<Key, Value>) -> Bool {
101+
return lhs.position == rhs.position
102+
}
103+
}
104+
105+
extension WeakDictionaryIndex: Comparable {
106+
public static func < (lhs: WeakDictionaryIndex<Key, Value>, rhs: WeakDictionaryIndex<Key, Value>) -> Bool {
107+
return lhs.position < rhs.position
108+
}
109+
}
110+
50111
extension WeakDictionary: Collection {
51112

52-
public typealias Index = DictionaryIndex<Key, WeakDictionaryReference<Value>>
113+
public typealias Index = WeakDictionaryIndex<Key, Value>
53114

54115
public var startIndex: Index {
55-
return storage.startIndex
116+
return Index(enumerator: storage.keyEnumerator(), position: 0)
56117
}
57118

58119
public var endIndex: Index {
59-
return storage.endIndex
120+
return Index()
60121
}
61122

62123
public func index(after index: Index) -> Index {
63-
return storage.index(after: index)
124+
return index.next()
64125
}
65126

66-
public subscript(position: Index) -> (Key, WeakDictionaryReference<Value>) {
67-
return storage[position]
127+
public subscript(position: Index) -> (Key, Value) {
128+
guard let key = position.key else {
129+
fatalError("Attempting to access WeakDictionary elements using an invalid index")
130+
}
131+
132+
return (key.base as! Key, storage.object(forKey: key)!)
68133
}
69134

70135
public subscript(key: Key) -> Value? {
71136
get {
72-
guard let valueRef = storage[key] else {
73-
return nil
74-
}
75-
76-
return valueRef.value
137+
return storage.object(forKey: AnyHashableBox(key))
77138
}
78139

79140
set {
80141
guard let value = newValue else {
81-
storage[key] = nil
142+
storage.removeObject(forKey: AnyHashableBox(key))
82143
return
83144
}
84145

85-
storage[key] = WeakDictionaryReference<Value>(value: value)
146+
storage.setObject(value, forKey: AnyHashableBox(key))
86147
}
87148
}
88149

89150
public subscript(bounds: Range<Index>) -> WeakDictionary<Key, Value> {
90-
let subStorage = storage[bounds.lowerBound ..< bounds.upperBound]
91-
var newStorage = [Key: WeakDictionaryReference<Value>]()
151+
let newStorage = NSMapTable<AnyHashableBox, Value>(keyOptions: .strongMemory, valueOptions: .weakMemory)
92152

93-
subStorage.filter { _, value in return value.value != nil }
94-
.forEach { key, value in newStorage[key] = value }
153+
var pos = bounds.lowerBound
154+
while pos.key != nil && pos != bounds.upperBound {
155+
newStorage.setObject(storage.object(forKey: pos.key), forKey: pos.key)
156+
pos = pos.next()
157+
}
95158

96159
return WeakDictionary<Key, Value>(storage: newStorage)
97160
}

0 commit comments

Comments
 (0)