Skip to content
This repository was archived by the owner on Jun 19, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions WeakDictionary.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,9 @@
E1B662061DBC53C000F9C758 /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E101D83C1DB76D1A00B5BDB8 /* WeakDictionary.swift */; };
E1B662071DBC53C600F9C758 /* WeakDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = E101D8251DB76CE800B5BDB8 /* WeakDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
E1CF11E021DCA87200488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
E1CF11E221DCA8AC00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
E1CF11E421DCA90B00488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
E1CF11E621DD783700488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
E1CF11E721DD783800488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
E1CF11E821DD783900488793 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */; };
E1CF11E921DD783C00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
E1CF11EA21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
E1CF11EB21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */; };
E1CF11EC21DD784100488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
E1CF11ED21DD784200488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
E1CF11EE21DD784300488793 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */; };
E1CF11F021DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
E1CF11F121DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
E1CF11F221DDAE9F00488793 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */; };
Expand Down Expand Up @@ -84,8 +76,6 @@
E1B661E61DBC509800F9C758 /* WeakDictionary-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "WeakDictionary-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
E1B661FE1DBC530E00F9C758 /* WeakDictionary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WeakDictionary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakDictionaryKeyReference.swift; sourceTree = "<group>"; };
E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakDictionaryReference.swift; sourceTree = "<group>"; };
E1CF11EF21DDAE9F00488793 /* DocumentationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentationTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -175,8 +165,6 @@
E101D8261DB76CE800B5BDB8 /* Info.plist */,
E101D83C1DB76D1A00B5BDB8 /* WeakDictionary.swift */,
E1CF11DF21DCA87200488793 /* WeakKeyDictionary.swift */,
E1CF11E121DCA8AC00488793 /* WeakDictionaryKeyReference.swift */,
E1CF11E321DCA90B00488793 /* WeakDictionaryReference.swift */,
);
path = WeakDictionary;
sourceTree = "<group>";
Expand Down Expand Up @@ -509,9 +497,7 @@
buildActionMask = 2147483647;
files = (
E101D83D1DB76D1A00B5BDB8 /* WeakDictionary.swift in Sources */,
E1CF11E421DCA90B00488793 /* WeakDictionaryReference.swift in Sources */,
E1CF11E021DCA87200488793 /* WeakKeyDictionary.swift in Sources */,
E1CF11E221DCA8AC00488793 /* WeakDictionaryKeyReference.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -531,9 +517,7 @@
buildActionMask = 2147483647;
files = (
E1B661D81DBC501300F9C758 /* WeakDictionary.swift in Sources */,
E1CF11EE21DD784300488793 /* WeakDictionaryReference.swift in Sources */,
E1CF11E621DD783700488793 /* WeakKeyDictionary.swift in Sources */,
E1CF11E921DD783C00488793 /* WeakDictionaryKeyReference.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -553,9 +537,7 @@
buildActionMask = 2147483647;
files = (
E1B661F51DBC50FD00F9C758 /* WeakDictionary.swift in Sources */,
E1CF11ED21DD784200488793 /* WeakDictionaryReference.swift in Sources */,
E1CF11E721DD783800488793 /* WeakKeyDictionary.swift in Sources */,
E1CF11EA21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -575,9 +557,7 @@
buildActionMask = 2147483647;
files = (
E1B662061DBC53C000F9C758 /* WeakDictionary.swift in Sources */,
E1CF11EC21DD784100488793 /* WeakDictionaryReference.swift in Sources */,
E1CF11E821DD783900488793 /* WeakKeyDictionary.swift in Sources */,
E1CF11EB21DD783D00488793 /* WeakDictionaryKeyReference.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
115 changes: 86 additions & 29 deletions WeakDictionary/WeakDictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,90 +8,147 @@

import Foundation

fileprivate class KeyBox<T: Hashable>: NSObject {
let key: T

init(_ key: T) {
self.key = key
super.init()
}

@objc override var hash: Int {
return key.hashValue
}

@objc override func isEqual(_ object: Any?) -> Bool {
return key == (object as! KeyBox<T>).key
}
}

public struct WeakDictionary<Key: Hashable, Value: AnyObject> {

private var storage: [Key: WeakDictionaryReference<Value>]
private var storage: NSMapTable<KeyBox<Key>, Value>

public init() {
self.init(storage: [Key: WeakDictionaryReference<Value>]())
self.init(storage: NSMapTable(keyOptions: .strongMemory, valueOptions: .weakMemory))
}

public init(dictionary: [Key: Value]) {
var newStorage = [Key: WeakDictionaryReference<Value>]()
dictionary.forEach({ key, value in newStorage[key] = WeakDictionaryReference<Value>(value: value) })
let newStorage = NSMapTable<KeyBox<Key>, Value>(keyOptions: .strongMemory, valueOptions: .weakMemory, capacity: dictionary.capacity)
dictionary.forEach({ key, value in newStorage.setObject(value, forKey: KeyBox<Key>(key)) })
self.init(storage: newStorage)
}

private init(storage: [Key: WeakDictionaryReference<Value>]) {
private init(storage: NSMapTable<KeyBox<Key>, Value>) {
self.storage = storage
}

public mutating func reap() {
storage = weakDictionary().storage
}

public func weakDictionary() -> WeakDictionary<Key, Value> {
return self[startIndex ..< endIndex]
}

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

storage.forEach { key, value in
if let retainedValue = value.value {
newStorage[key] = retainedValue
for key in storage.keyEnumerator() {
if let value = storage.object(forKey: (key as! KeyBox<Key>)) {
newStorage[(key as! KeyBox<Key>).key] = value
}
}

return newStorage
}
}

public class WeakDictionaryIndex<Key: Hashable, Value> {
fileprivate let key: KeyBox<Key>?

private let position: UInt
private var enumerator: NSEnumerator?

fileprivate lazy var next: WeakDictionaryIndex<Key, Value> = {
defer { self.enumerator = nil }
return .init(enumerator: enumerator, position: position + 1)
}()

fileprivate convenience init(enumerator: NSEnumerator?, position: UInt = 0) {
if let key = enumerator?.nextObject() as! KeyBox<Key>? {
self.init(key: key, enumerator: enumerator, position: position)
} else {
self.init(key: nil, enumerator: nil, position: .max)
}
}

fileprivate convenience init() {
self.init(key: nil, enumerator: nil, position: .max)
}

private init(key: KeyBox<Key>?, enumerator: NSEnumerator?, position: UInt) {
self.key = key
self.enumerator = enumerator
self.position = position
}
}

extension WeakDictionaryIndex: Equatable {
public static func == (lhs: WeakDictionaryIndex<Key, Value>, rhs: WeakDictionaryIndex<Key, Value>) -> Bool {
return lhs.position == rhs.position
}
}

extension WeakDictionaryIndex: Comparable {
public static func < (lhs: WeakDictionaryIndex<Key, Value>, rhs: WeakDictionaryIndex<Key, Value>) -> Bool {
return lhs.position < rhs.position
}
}

extension WeakDictionary: Collection {

public typealias Index = DictionaryIndex<Key, WeakDictionaryReference<Value>>
public typealias Index = WeakDictionaryIndex<Key, Value>

public var startIndex: Index {
return storage.startIndex
return Index(enumerator: storage.keyEnumerator())
}

public var endIndex: Index {
return storage.endIndex
return Index()
}

public func index(after index: Index) -> Index {
return storage.index(after: index)
return index.next
}

public subscript(position: Index) -> (Key, WeakDictionaryReference<Value>) {
return storage[position]
public subscript(position: Index) -> (Key, Value) {
guard let key = position.key else {
fatalError("Attempting to access WeakDictionary elements using an invalid index")
}

return (key.key, storage.object(forKey: key)!)
}

public subscript(key: Key) -> Value? {
get {
guard let valueRef = storage[key] else {
return nil
}

return valueRef.value
return storage.object(forKey: KeyBox<Key>(key))
}

set {
guard let value = newValue else {
storage[key] = nil
storage.removeObject(forKey: KeyBox<Key>(key))
return
}

storage[key] = WeakDictionaryReference<Value>(value: value)
storage.setObject(value, forKey: KeyBox<Key>(key))
}
}

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

subStorage.filter { _, value in return value.value != nil }
.forEach { key, value in newStorage[key] = value }
var pos = bounds.lowerBound
while pos.key != nil && pos != bounds.upperBound {
newStorage.setObject(storage.object(forKey: pos.key), forKey: pos.key)
pos = pos.next
}

return WeakDictionary<Key, Value>(storage: newStorage)
}
Expand Down
36 changes: 0 additions & 36 deletions WeakDictionary/WeakDictionaryKeyReference.swift

This file was deleted.

21 changes: 0 additions & 21 deletions WeakDictionary/WeakDictionaryReference.swift

This file was deleted.

Loading