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

Commit 1658e26

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

File tree

5 files changed

+180
-140
lines changed

5 files changed

+180
-140
lines changed

WeakDictionary.xcodeproj/project.pbxproj

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,9 @@
2828
E1B662061DBC53C000F9C758 /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E101D83C1DB76D1A00B5BDB8 /* WeakDictionary.swift */; };
2929
E1B662071DBC53C600F9C758 /* WeakDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = E101D8251DB76CE800B5BDB8 /* WeakDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
3030
E1CF11E021DCA87200488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
31-
E1CF11E221DCA8AC00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
32-
E1CF11E421DCA90B00488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
3331
E1CF11E621DD783700488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
3432
E1CF11E721DD783800488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
3533
E1CF11E821DD783900488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
36-
E1CF11E921DD783C00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
37-
E1CF11EA21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
38-
E1CF11EB21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
39-
E1CF11EC21DD784100488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
40-
E1CF11ED21DD784200488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
41-
E1CF11EE21DD784300488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
4234
E1CF11F021DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
4335
E1CF11F121DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
4436
E1CF11F221DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
@@ -84,8 +76,6 @@
8476
E1B661E61DBC509800F9C758 /* WeakDictionary-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "WeakDictionary-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
8577
E1B661FE1DBC530E00F9C758 /* WeakDictionary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WeakDictionary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8678
E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
87-
E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakDictionaryKeyReference.swift; sourceTree = "<group>"; };
88-
E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakDictionaryReference.swift; sourceTree = "<group>"; };
8979
E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentationTests.swift; sourceTree = "<group>"; };
9080
/* End PBXFileReference section */
9181

@@ -175,8 +165,6 @@
175165
E101D8261DB76CE800B5BDB8 /* Info.plist */,
176166
E101D83C1DB76D1A00B5BDB8 /* WeakDictionary.swift */,
177167
E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */,
178-
E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */,
179-
E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */,
180168
);
181169
path = WeakDictionary;
182170
sourceTree = "<group>";
@@ -509,9 +497,7 @@
509497
buildActionMask = 2147483647;
510498
files = (
511499
E101D83D1DB76D1A00B5BDB8 /* WeakDictionary.swift in Sources */,
512-
E1CF11E421DCA90B00488793 /* WeakDictionaryReference.swift in Sources */,
513500
E1CF11E021DCA87200488793 /* WeakKeyDictionary.swift in Sources */,
514-
E1CF11E221DCA8AC00488793 /* WeakDictionaryKeyReference.swift in Sources */,
515501
);
516502
runOnlyForDeploymentPostprocessing = 0;
517503
};
@@ -531,9 +517,7 @@
531517
buildActionMask = 2147483647;
532518
files = (
533519
E1B661D81DBC501300F9C758 /* WeakDictionary.swift in Sources */,
534-
E1CF11EE21DD784300488793 /* WeakDictionaryReference.swift in Sources */,
535520
E1CF11E621DD783700488793 /* WeakKeyDictionary.swift in Sources */,
536-
E1CF11E921DD783C00488793 /* WeakDictionaryKeyReference.swift in Sources */,
537521
);
538522
runOnlyForDeploymentPostprocessing = 0;
539523
};
@@ -553,9 +537,7 @@
553537
buildActionMask = 2147483647;
554538
files = (
555539
E1B661F51DBC50FD00F9C758 /* WeakDictionary.swift in Sources */,
556-
E1CF11ED21DD784200488793 /* WeakDictionaryReference.swift in Sources */,
557540
E1CF11E721DD783800488793 /* WeakKeyDictionary.swift in Sources */,
558-
E1CF11EA21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */,
559541
);
560542
runOnlyForDeploymentPostprocessing = 0;
561543
};
@@ -575,9 +557,7 @@
575557
buildActionMask = 2147483647;
576558
files = (
577559
E1B662061DBC53C000F9C758 /* WeakDictionary.swift in Sources */,
578-
E1CF11EC21DD784100488793 /* WeakDictionaryReference.swift in Sources */,
579560
E1CF11E821DD783900488793 /* WeakKeyDictionary.swift in Sources */,
580-
E1CF11EB21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */,
581561
);
582562
runOnlyForDeploymentPostprocessing = 0;
583563
};

WeakDictionary/WeakDictionary.swift

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

99
import Foundation
1010

11+
fileprivate class KeyBox<T: Hashable>: NSObject {
12+
let key: T
13+
14+
init(_ key: T) {
15+
self.key = key
16+
super.init()
17+
}
18+
19+
@objc override var hash: Int {
20+
return key.hashValue
21+
}
22+
23+
@objc override func isEqual(_ object: Any?) -> Bool {
24+
return key == (object as! KeyBox<T>).key
25+
}
26+
}
27+
1128
public struct WeakDictionary<Key: Hashable, Value: AnyObject> {
1229

13-
private var storage: [Key: WeakDictionaryReference<Value>]
30+
private var storage: NSMapTable<KeyBox<Key>, Value>
1431

1532
public init() {
16-
self.init(storage: [Key: WeakDictionaryReference<Value>]())
33+
self.init(storage: NSMapTable(keyOptions: .strongMemory, valueOptions: .weakMemory))
1734
}
1835

1936
public init(dictionary: [Key: Value]) {
20-
var newStorage = [Key: WeakDictionaryReference<Value>]()
21-
dictionary.forEach({ key, value in newStorage[key] = WeakDictionaryReference<Value>(value: value) })
37+
let newStorage = NSMapTable<KeyBox<Key>, Value>(keyOptions: .strongMemory, valueOptions: .weakMemory, capacity: dictionary.capacity)
38+
dictionary.forEach({ key, value in newStorage.setObject(value, forKey: KeyBox<Key>(key)) })
2239
self.init(storage: newStorage)
2340
}
2441

25-
private init(storage: [Key: WeakDictionaryReference<Value>]) {
42+
private init(storage: NSMapTable<KeyBox<Key>, Value>) {
2643
self.storage = storage
2744
}
2845

29-
public mutating func reap() {
30-
storage = weakDictionary().storage
31-
}
32-
3346
public func weakDictionary() -> WeakDictionary<Key, Value> {
3447
return self[startIndex ..< endIndex]
3548
}
3649

3750
public func dictionary() -> [Key: Value] {
3851
var newStorage = [Key: Value]()
3952

40-
storage.forEach { key, value in
41-
if let retainedValue = value.value {
42-
newStorage[key] = retainedValue
53+
for key in storage.keyEnumerator() {
54+
if let value = storage.object(forKey: (key as! KeyBox<Key>)) {
55+
newStorage[(key as! KeyBox<Key>).key] = value
4356
}
4457
}
4558

4659
return newStorage
4760
}
4861
}
4962

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

52-
public typealias Index = DictionaryIndex<Key, WeakDictionaryReference<Value>>
107+
public typealias Index = WeakDictionaryIndex<Key, Value>
53108

54109
public var startIndex: Index {
55-
return storage.startIndex
110+
return Index(enumerator: storage.keyEnumerator())
56111
}
57112

58113
public var endIndex: Index {
59-
return storage.endIndex
114+
return Index()
60115
}
61116

62117
public func index(after index: Index) -> Index {
63-
return storage.index(after: index)
118+
return index.next
64119
}
65120

66-
public subscript(position: Index) -> (Key, WeakDictionaryReference<Value>) {
67-
return storage[position]
121+
public subscript(position: Index) -> (Key, Value) {
122+
guard let key = position.key else {
123+
fatalError("Attempting to access WeakDictionary elements using an invalid index")
124+
}
125+
126+
return (key.key, storage.object(forKey: key)!)
68127
}
69128

70129
public subscript(key: Key) -> Value? {
71130
get {
72-
guard let valueRef = storage[key] else {
73-
return nil
74-
}
75-
76-
return valueRef.value
131+
return storage.object(forKey: KeyBox<Key>(key))
77132
}
78133

79134
set {
80135
guard let value = newValue else {
81-
storage[key] = nil
136+
storage.removeObject(forKey: KeyBox<Key>(key))
82137
return
83138
}
84139

85-
storage[key] = WeakDictionaryReference<Value>(value: value)
140+
storage.setObject(value, forKey: KeyBox<Key>(key))
86141
}
87142
}
88143

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

93-
subStorage.filter { _, value in return value.value != nil }
94-
.forEach { key, value in newStorage[key] = value }
147+
var pos = bounds.lowerBound
148+
while pos.key != nil && pos != bounds.upperBound {
149+
newStorage.setObject(storage.object(forKey: pos.key), forKey: pos.key)
150+
pos = pos.next
151+
}
95152

96153
return WeakDictionary<Key, Value>(storage: newStorage)
97154
}

WeakDictionary/WeakDictionaryKeyReference.swift

Lines changed: 0 additions & 36 deletions
This file was deleted.

WeakDictionary/WeakDictionaryReference.swift

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)