Last active
September 23, 2022 04:30
-
-
Save orchetect/670571acc03840d8674ea3b08302f661 to your computer and use it in GitHub Desktop.
Weak.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Weak.swift | |
| // | |
| // Created by Steffan Andrews on 2019-11-16. | |
| // Copyright © 2019 Steffan Andrews. All rights reserved. | |
| // | |
| // MARK: - Weak<T> | |
| /// Wrapper that contains a weak reference to an object. | |
| internal struct Weak<T: AnyObject> { | |
| weak var wrapped: T? | |
| init(_ object: T?) { | |
| wrapped = object | |
| } | |
| } | |
| // MARK: - WeakHashable<T> | |
| /// Wrapper that contains a weak reference to a `Hashable` object. | |
| internal struct WeakHashable<T: AnyObject>: Hashable where T: Hashable { | |
| weak var wrapped: T? | |
| init(_ object: T?) { | |
| wrapped = object | |
| } | |
| func hash(into hasher: inout Hasher) { | |
| hasher.combine(wrapped) | |
| } | |
| } | |
| // MARK: - WeakArray | |
| /// Transparently encapsulates each element in a `Weak` box, storing the value as a `weak var` reference. | |
| public struct WeakArray<Element: AnyObject> { | |
| // backing array of Weak elements | |
| private var storage: [Weak<Element>] = [] | |
| // user-exposed unwrapped elements | |
| public var elements: [Element?] { | |
| get { | |
| storage.map { $0.wrapped } | |
| } | |
| set { | |
| storage = newValue.map { Weak($0) } | |
| } | |
| } | |
| init() { } | |
| init(_ elements: [Element?]) { | |
| storage = elements.map { Weak($0) } | |
| } | |
| public subscript(_ index: Array<Element>.Index) -> Element? { | |
| get { | |
| storage[index].wrapped | |
| } | |
| set { | |
| storage[index] = Weak(newValue) | |
| } | |
| } | |
| /// Removes weak elements boxes that contain `nil`. | |
| public mutating func cleanup() { | |
| for idx in storage.indices.reversed() { | |
| if storage[idx].wrapped == nil { | |
| storage.remove(at: idx) | |
| } | |
| } | |
| } | |
| } | |
| extension WeakArray: ExpressibleByArrayLiteral { | |
| public typealias ArrayLiteralElement = Element | |
| public init(arrayLiteral elements: Element...) { | |
| self.init(elements) | |
| } | |
| } | |
| // MARK: - WeakArrayStorage propertywrapper | |
| /// Transparently encapsulates each element in a `Weak` box, storing the value as a `weak var` reference. | |
| @propertyWrapper | |
| public struct WeakArrayStorage<Element: AnyObject> { | |
| private var value: WeakArray<Element> | |
| public var wrappedValue: [Element?] { | |
| get { | |
| value.elements | |
| } | |
| set { | |
| value.elements = newValue | |
| } | |
| } | |
| public init(wrappedValue: [Element?]) { | |
| value = WeakArray(wrappedValue) | |
| } | |
| /// Removes weak elements boxes that contain `nil`. | |
| public mutating func cleanup() { | |
| value.cleanup() | |
| } | |
| } | |
| // MARK: - WeakSet | |
| /// Array that holds WeakBox objects. Accessing directly will act as a set of unwrapped objects. | |
| public struct WeakSet<Element: AnyObject> where Element: Hashable { | |
| // backing array of Weak elements | |
| private var storage: Set<WeakHashable<Element>> = [] | |
| // user-exposed unwrapped elements | |
| public var elements: Set<Element?> { | |
| get { | |
| Set(storage.map { $0.wrapped }) | |
| } | |
| set { | |
| storage = Set(newValue.map { WeakHashable($0) }) | |
| } | |
| } | |
| init() { } | |
| init(_ elements: [Element?]) { | |
| storage = Set(elements.map { WeakHashable($0) }) | |
| } | |
| init(_ set: Set<Element?>) { | |
| storage = Set(elements.map { WeakHashable($0) }) | |
| } | |
| /// Removes elements that contain `nil`. | |
| public mutating func cleanup() { | |
| for idx in storage.indices.reversed() { | |
| if storage[idx].wrapped == nil { | |
| storage.remove(at: idx) | |
| } | |
| } | |
| } | |
| } | |
| extension WeakSet: ExpressibleByArrayLiteral { | |
| public typealias ArrayLiteralElement = Element | |
| public init(arrayLiteral elements: Element...) { | |
| self.init(elements) | |
| } | |
| } | |
| // MARK: - WeakSetStorage propertywrapper | |
| /// Transparently encapsulates each element in a `Weak` box, storing the value as a `weak var` reference. | |
| @propertyWrapper | |
| public struct WeakSetStorage<Element: AnyObject> where Element: Hashable { | |
| private var value: WeakSet<Element> | |
| public var wrappedValue: Set<Element?> { | |
| get { | |
| value.elements | |
| } | |
| set { | |
| value.elements = newValue | |
| } | |
| } | |
| public init(wrappedValue: Set<Element?>) { | |
| value = WeakSet(wrappedValue.map { $0 }) | |
| } | |
| /// Removes weak elements that contain `nil`. | |
| public mutating func cleanup() { | |
| value.cleanup() | |
| } | |
| } | |
| // MARK: - WeakDictionary | |
| /// Array that holds WeakBox objects. Accessing directly will act as a dictionary of unwrapped objects. | |
| public struct WeakDictionary<KeyType: Hashable, Element: AnyObject> { | |
| public typealias DictionaryType = [KeyType: Element?] | |
| fileprivate typealias StorageDictionaryType = [KeyType: Weak<Element>] | |
| // backing array of Weak elements | |
| private var storage: StorageDictionaryType = [:] | |
| public var elements: DictionaryType { | |
| get { | |
| storage.mapValues { $0.wrapped } | |
| } | |
| set { | |
| storage = newValue.mapValues { Weak($0) } | |
| } | |
| } | |
| init() { } | |
| init(_ dictionary: [KeyType: Element?]) { | |
| storage = dictionary.mapValues { Weak($0) } | |
| } | |
| } | |
| extension WeakDictionary { | |
| public subscript(_ key: KeyType) -> Element? { | |
| get { | |
| storage[key]?.wrapped | |
| } | |
| set { | |
| storage[key] = | |
| newValue == nil | |
| ? nil | |
| : Weak(newValue) | |
| } | |
| } | |
| /// Removes weak elements that contain `nil`. | |
| public mutating func cleanup() { | |
| for key in storage.keys { | |
| if storage[key]?.wrapped == nil { | |
| storage[key] = nil | |
| } | |
| } | |
| } | |
| } | |
| extension WeakDictionary: ExpressibleByDictionaryLiteral { | |
| public typealias Key = KeyType | |
| public typealias Value = Element | |
| public init(dictionaryLiteral elements: (KeyType, Element)...) { | |
| elements.forEach { storage[$0.0] = Weak($0.1) } | |
| } | |
| } | |
| // MARK: - WeakDictionary propertywrapper | |
| /// Transparently encapsulates each element in a `Weak` box, storing the value as a `weak var` reference. | |
| @propertyWrapper | |
| public struct WeakDictionaryStorage<KeyType: Hashable, Element: AnyObject> { | |
| private var value: WeakDictionary<KeyType, Element> | |
| public var wrappedValue: [KeyType: Element?] { | |
| get { | |
| value.elements | |
| } | |
| set { | |
| value.elements = newValue | |
| } | |
| } | |
| public init(wrappedValue: [KeyType: Element?]) { | |
| value = WeakDictionary(wrappedValue) | |
| } | |
| /// Removes weak elements that contain `nil`. | |
| public mutating func cleanup() { | |
| value.cleanup() | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment