web-dev-qa-db-ja.com

Swiftで弱参照の配列を宣言するにはどうすればよいですか?

Swiftに一連の弱い参照を格納したいと思います。配列自体は弱参照であってはなりません-その要素は弱参照であるべきです。 Cocoa NSPointerArrayは、このタイプセーフでないバージョンを提供すると思います。

168
Bill

次のように汎用ラッパーを作成します。

class Weak<T: AnyObject> {
  weak var value : T?
  init (value: T) {
    self.value = value
  }
}

このクラスのインスタンスを配列に追加します。

class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]

Weakを定義するときは、structまたはclassのいずれかを使用できます。

また、配列の内容の取得を支援するために、次の行に沿って何かを行うことができます。

extension Array where Element:Weak<AnyObject> {
  mutating func reap () {
    self = self.filter { nil != $0.value }
  }
}

上記のAnyObjectの使用はTに置き換える必要がありますが、現在のSwift言語ではそのように定義された拡張が許可されていないと思います。

141
GoZoner

NSHashTableはweakObjectsHashTableで使用できます。 NSHashTable.weakObjectsHashTable()

Swift 3の場合:NSHashTable.weakObjects()

NSHashTableクラスリファレンス

OS X v10.5以降で使用できます。

IOS 6.0以降で利用可能。

47
Thierry

パーティーには少し遅れていますが、試してみてください。配列ではなくセットとして実装しました。

WeakObjectSet

class WeakObject<T: AnyObject>: Equatable, Hashable {
    weak var object: T?
    init(object: T) {
        self.object = object
    }

    var hashValue: Int {
        if let object = self.object { return unsafeAddressOf(object).hashValue }
        else { return 0 }
    }
}

func == <T> (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
    return lhs.object === rhs.object
}


class WeakObjectSet<T: AnyObject> {
    var objects: Set<WeakObject<T>>

    init() {
        self.objects = Set<WeakObject<T>>([])
    }

    init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    var allObjects: [T] {
        return objects.flatMap { $0.object }
    }

    func contains(object: T) -> Bool {
        return self.objects.contains(WeakObject(object: object))
    }

    func addObject(object: T) {
        self.objects.unionInPlace([WeakObject(object: object)])
    }

    func addObjects(objects: [T]) {
        self.objects.unionInPlace(objects.map { WeakObject(object: $0) })
    }
}

使用法

var alice: NSString? = "Alice"
var bob: NSString? = "Bob"
var cathline: NSString? = "Cathline"

var persons = WeakObjectSet<NSString>()
persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObjects([alice!, cathline!])
print(persons.allObjects) // [Alice, Cathline, Bob]

alice = nil
print(persons.allObjects) // [Cathline, Bob]

bob = nil
print(persons.allObjects) // [Cathline]

WeakObjectSetはString型ではなくNSString型を取ることに注意してください。なぜなら、文字列型はAnyTypeではないからです。私のSwiftバージョンはApple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)です。

コードはGistから取得できます。 https://Gist.github.com/codelynx/30d3c42a833321f17d39

** 2017年11月に追加

コードをSwift 4に更新しました

// Swift 4, Xcode Version 9.1 (9B55)

class WeakObject<T: AnyObject>: Equatable, Hashable {
    weak var object: T?
    init(object: T) {
        self.object = object
    }

    var hashValue: Int {
        if var object = object { return UnsafeMutablePointer<T>(&object).hashValue }
        return 0
    }

    static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
        return lhs.object === rhs.object
    }
}

class WeakObjectSet<T: AnyObject> {
    var objects: Set<WeakObject<T>>

    init() {
        self.objects = Set<WeakObject<T>>([])
    }

    init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    var allObjects: [T] {
        return objects.flatMap { $0.object }
    }

    func contains(_ object: T) -> Bool {
        return self.objects.contains(WeakObject(object: object))
    }

    func addObject(_ object: T) {
        self.objects.formUnion([WeakObject(object: object)])
    }

    func addObjects(_ objects: [T]) {
        self.objects.formUnion(objects.map { WeakObject(object: $0) })
    }
}

Gokejiが述べたように、使用中のコードに基づいてNSStringが割り当て解除されないことがわかりました。私は頭をかいて、次のようにMyStringクラスを作成しました。

// typealias MyString = NSString
class MyString: CustomStringConvertible {
    var string: String
    init(string: String) {
        self.string = string
    }
    deinit {
        print("relasing: \(string)")
    }
    var description: String {
        return self.string
    }
}

次に、NSStringMyStringに置き換えます。それからそれが機能すると言うのは奇妙です。

var alice: MyString? = MyString(string: "Alice")
var bob: MyString? = MyString(string: "Bob")
var cathline: MyString? = MyString(string: "Cathline")

var persons = WeakObjectSet<MyString>()

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObjects([alice!, cathline!])
print(persons.allObjects) // [Alice, Cathline, Bob]

alice = nil
print(persons.allObjects) // [Cathline, Bob]

bob = nil
print(persons.allObjects) // [Cathline]

次に、この問題に関連する奇妙なページを見つけました。

弱い参照は割り当て解除されたNSStringを保持します(XC9 + iOS Simのみ)

https://bugs.Swift.org/browse/SR-5511

問題はRESOLVEDであると書かれていますが、これがこの問題にまだ関係しているかどうか疑問に思っています。とにかく、MyStringとNSStringの動作の違いはこのコンテキストを超えていますが、誰かがこの問題を理解してくれたら幸いです。

11
Kaz Yoshikawa

これは私の解決策ではありません。 Apple開発者フォーラムで見つけました

@GoZonerには良い答えがありますが、Swiftコンパイラがクラッシュします。

これは、現在リリースされているコンパイラをクラッシュさせない弱オブジェクトコンテナのバージョンです。

struct WeakContainer<T where T: AnyObject> {
    weak var _value : T?

    init (value: T) {
        _value = value
    }

    func get() -> T? {
        return _value
    }
}

次に、これらのコンテナの配列を作成できます。

let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
11
rjkaplan

これを行うには、弱いポインターを保持するラッパーオブジェクトを作成します。

struct WeakThing<T: AnyObject> {
  weak var value: T?
  init (value: T) {
    self.value = value
  }
}

そして、配列でこれらを使用して

var weakThings = WeakThing<Foo>[]()
9
Joshua Weinberg

私はジェネリックで弱いコンテナを作成するという同じ考えを持っていました。
その結果、NSHashTableのラッパーを作成しました。

class WeakSet<ObjectType>: SequenceType {

    var count: Int {
        return weakStorage.count
    }

    private let weakStorage = NSHashTable.weakObjectsHashTable()

    func addObject(object: ObjectType) {
        guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
        weakStorage.addObject(object as? AnyObject)
    }

    func removeObject(object: ObjectType) {
        guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
        weakStorage.removeObject(object as? AnyObject)
    }

    func removeAllObjects() {
        weakStorage.removeAllObjects()
    }

    func containsObject(object: ObjectType) -> Bool {
        guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
        return weakStorage.containsObject(object as? AnyObject)
    }

    func generate() -> AnyGenerator<ObjectType> {
        let enumerator = weakStorage.objectEnumerator()
        return anyGenerator {
            return enumerator.nextObject() as! ObjectType?
        }
    }
}

使用法:

protocol MyDelegate : AnyObject {
    func doWork()
}

class MyClass: AnyObject, MyDelegate {
    fun doWork() {
        // Do delegated work.
    }
}

var delegates = WeakSet<MyDelegate>()
delegates.addObject(MyClass())

for delegate in delegates {
    delegate.doWork()
}

WeakSetは任意のタイプで初期化でき、このタイプがAnyObjectプロトコルに準拠していない場合、詳細な理由でアプリがクラッシュするため、これは最適なソリューションではありません。しかし、今のところこれ以上良い解決策はありません。

元の解決策は、次の方法でWeakSetを定義することでした。

class WeakSet<ObjectType: AnyObject>: SequenceType {}

ただし、この場合、WeakSetはプロトコルで初期化できません。

protocol MyDelegate : AnyObject {
    func doWork()
}

let weakSet = WeakSet<MyDelegate>()

現在、上記のコードはコンパイルできません(Swift 2.1、Xcode 7.1)。
だからこそ、AnyObjectへの準拠をやめ、fatalError()アサーションでガードを追加しました。

7
Vlad Papko

機能的なスタイルのラッパーはどうですか?

class Class1 {}

func captureWeakly<T> (_ target:T) -> (() -> T?) where T: AnyObject {
    return { [weak target] in
        return target
    }
}

let obj1 = Class1()
let obj2 = Class1()
let obj3 = Class1()
let captured1 = captureWeakly(obj1)
let captured2 = captureWeakly(obj2)
let captured3 = captureWeakly(obj3)

返されたクロージャを呼び出して、ターゲットがまだ生きていることを確認します。

let isAlive = captured1() != nil
let theValue = captured1()!

そして、このクロージャーを配列に保存できます。

let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])

また、クロージャーを呼び出してマッピングすることにより、弱くキャプチャされた値を取得できます。

let values = Array(array1.map({ $0() }))
7
Eonil

WeakContainerの既存の例は役立ちますが、ListsやDictionariesなどの既存のSwiftコンテナーで弱参照を使用することは実際には役立ちません。

ContainsなどのListメソッドを使用する場合、WeakContainerはEquatableを実装する必要があります。そこで、WeakContainerを赤道できるようにするコードを追加しました。

辞書でWeakContainerを使用したい場合は、辞書キーとして使用できるようにハッシュ可能にしました。

また、これはWeakObjectに名前を変更し、これがクラスタイプ専用であることを強調し、WeakContainerの例と区別します。

struct WeakObject<TYPE where TYPE:AnyObject> : Equatable, Hashable
{
    weak var _value : TYPE?
    let _originalHashValue : Int

    init (value: TYPE)
    {
        _value = value

        // We keep around the original hash value so that we can return it to represent this
        // object even if the value became Nil out from under us because the object went away.
        _originalHashValue = ObjectIdentifier(value).hashValue
    }

    var value : TYPE?
    {
        return _value
    }

    var hashValue: Int
    {
        return _originalHashValue
    }
}

func ==<T>(lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool
{
    if lhs.value == nil  &&  rhs.value == nil {
        return true
    }
    else if lhs.value == nil  ||  rhs.value == nil {
        return false
    }

    // If the objects are the same, then we are good to go
    return lhs.value! === rhs.value!
}

これにより、弱参照のディクショナリを使用するなどのクールなことができます。

private var m_observerDict : Dictionary<WeakObject<AnyObject>,FLObservationBlock> = Dictionary()

func addObserver( observer:AnyObject, block:FLObservationBlock )
{
    let weakObserver = WeakObject(value:observer)
    m_observerDict[weakObserver] = block
}


func removeObserver( observer:AnyObject )
{
    let weakObserver = WeakObject(value:observer)
    m_observerDict.removeValueForKey(weakObserver)
}
3
Tod Cunningham

[吉川和] [1]の回答に基づく

詳細

  • Xcode 10.2(10E125)、Swift 5

解決

import Foundation

protocol Weakable: class {
    associatedtype T: AnyObject = Self
    var asWeakValue: WeakObject<T> { get }
}

protocol WeakObjectProtocol {
    associatedtype WeakObjectType
    var value: WeakObjectType? {get set}
    init(object: WeakObjectType)
}

class WeakObject<T: AnyObject>: WeakObjectProtocol {
    typealias WeakObjectType = T
    weak var value: WeakObjectType?

    required init(object: WeakObjectType) {
        self.value = object
    }

    var referenceCount: Int {
        return CFGetRetainCount(value)
    }
}

extension WeakObject: Equatable {
    static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
        return lhs.value === rhs.value
    }
}

extension WeakObject: CustomStringConvertible {
    var description: String {
        if let value = value  {
            let className = String(describing: type(of: value.self))
            return "{class: \(className); referenceCount: \(referenceCount)}"
        }
        return "nil"
    }
}

extension Array where Element: AnyObject  {

    var asWeakArray: Array<WeakObject<Element>> {
        var weakArray = [WeakObject<Element>]()
        for item in self {
            let obj = WeakObject(object: item)
            weakArray.append(obj)
        }
        return weakArray
    }
}

使用法

extension UIView: Weakable {
    var asWeakValue: WeakObject<UIView> { return WeakObject(object: self) }
}

var weakArray = [WeakObject<UIView>]()
weakArray = view.subviews.asWeakArray
weakArray.append(view.asWeakValue)

完全なサンプル

ソリューションコードを貼り付けることを忘れないでください

import UIKit

class ViewController: UIViewController {

    private var weakArray = [WeakObject<UIView>]()
    override func viewDidLoad() {
        super.viewDidLoad()
        addSubviews()
        weakArray = view.subviews.asWeakArray
        weakArray.append(createRandomRectangleAndAdd(to: view).asWeakValue)
    }

    private func printArray(title: String) {
        print("=============================\n\(title)\ncount: \(weakArray.count)")
        weakArray.enumerated().forEach { print("\($0) \(String(describing: $1))") }
    }
}

extension ViewController {

    private func createRandomRectangleAndAdd(to parentView: UIView) -> UIView {
        let view = UIView(frame: CGRect(x: Int.random(in: 0...200),
                                        y: Int.random(in: 60...200),
                                        width: Int.random(in: 0...200),
                                        height: Int.random(in: 0...200)))
        let color = UIColor(red: CGFloat.random(in: 0...255)/255,
                            green: CGFloat.random(in: 0...255)/255,
                            blue: CGFloat.random(in: 0...255)/255,
                            alpha: 1)
        view.backgroundColor = color
        parentView.addSubview(view)
        return view
    }

    private func addSubviews() {

        _ = createRandomRectangleAndAdd(to: view)
        _ = createRandomRectangleAndAdd(to: view)

        addButtons()
    }

    private func createButton(title: String, frame: CGRect, action: Selector) -> UIButton {
        let button = UIButton(frame: frame)
        button.setTitle(title, for: .normal)
        button.addTarget(self, action: action, for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        return button
    }

    private func addButtons() {
        view.addSubview(createButton(title: "Add",
                                     frame: CGRect(x: 10, y: 20, width: 40, height: 40),
                                     action: #selector(addView)))

        view.addSubview(createButton(title: "Delete",
                                     frame: CGRect(x: 60, y: 20, width: 60, height: 40),
                                     action: #selector(deleteView)))

        view.addSubview(createButton(title: "Remove nils",
                                     frame: CGRect(x: 120, y: 20, width: 100, height: 40),
                                     action: #selector(removeNils)))
    }

    @objc func deleteView() {
        view.subviews
            .filter { view -> Bool in return !(view is UIButton) }
            .first?.removeFromSuperview()

        view.layoutIfNeeded()
        printArray(title: "First view deleted")
    }

    @objc func addView() {
        weakArray.append(createRandomRectangleAndAdd(to: view).asWeakValue)
        printArray(title: "View addded")
    }

    @objc func removeNils() {
        weakArray = weakArray.filter { $0.value != nil }
        printArray(title: "Remove all nil elements in weakArray")
    }
}

extension UIView: Weakable {
    var asWeakValue: WeakObject<UIView> { return WeakObject(object: self) }
}
3

@GoZonerの優れた回答をHashableに準拠させる方法は次のとおりです。したがって、SetDictionaryArrayなどのコンテナオブジェクトでインデックスを作成できます。

private class Weak<T: AnyObject>: Hashable {
    weak var value : T!
    init (value: T) {
       self.value = value
    }

    var hashValue : Int {
       // ObjectIdentifier creates a unique hashvalue for objects.
       return ObjectIdentifier(self.value).hashValue
    }
}

// Need to override so we can conform to Equitable.
private func == <T>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.hashValue == rhs.hashValue
}
2
Sakiboy

同じ問題のさらに別の解決策...この問題の焦点は、オブジェクトへの弱い参照を格納することですが、構造体も格納できるようにすることです。

[どれほど便利かはわかりませんが、構文が正しくなるまで時間がかかりました]

class WeakWrapper : Equatable {
    var valueAny : Any?
    weak var value : AnyObject?

    init(value: Any) {
        if let valueObj = value as? AnyObject {
            self.value = valueObj
        } else {
            self.valueAny = value
        }
    }

    func recall() -> Any? {
        if let value = value {
            return value
        } else if let value = valueAny {
            return value
        }
        return nil
    }
}


func ==(lhs: WeakWrapper, rhs: WeakWrapper) -> Bool {
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}



class Stuff {}
var weakArray : [WeakWrapper] = [WeakWrapper(value: Stuff()), WeakWrapper(value: CGRectZero)]

extension Array where Element : WeakWrapper  {

    mutating func removeObject(object: Element) {
        if let index = self.indexOf(object) {
            self.removeAtIndex(index)
        }
    }

    mutating func compress() {
        for obj in self {
            if obj.recall() == nil {
                self.removeObject(obj)
            }
        }
    }


}

weakArray[0].recall()
weakArray[1].recall() == nil
weakArray.compress()
weakArray.count
1
Dan Rosenstark

NSPointerArrayはすでにこれのほとんどを自動的に処理するため、タイプセーフなラッパーを作成することで問題を解決しました。

class WeakArray<T: AnyObject> {
    private let pointers = NSPointerArray.weakObjects()

    init (_ elements: T...) {
        elements.forEach{self.pointers.addPointer(Unmanaged.passUnretained($0).toOpaque())}
    }

    func get (_ index: Int) -> T? {
        if index < self.pointers.count, let pointer = self.pointers.pointer(at: 0) {
            return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
        } else {
            return nil
        }
    }
    func append (_ element: T) {
        self.pointers.addPointer(Unmanaged.passUnretained(element).toOpaque())
    }
    func forEach (_ callback: (T) -> ()) {
        for i in 0..<self.pointers.count {
            if let element = self.get(i) {
                callback(element)
            }
        }
    }
    // implement other functionality as needed
}

使用例:

class Foo {}
var foo: Foo? = Foo()
let array = WeakArray(foo!)
print(array.get(0)) // Optional(Foo)
foo = nil
DispatchQueue.main.async{print(array.get(0))} // nil

前もってより多くの作業を行いますが、残りのコードでの使用はIMOのほうがずっときれいです。配列のようにしたい場合は、添え字を実装したり、SequenceTypeなどにすることもできます(ただし、私のプロジェクトではappendforEachのみが必要なので、正確なコードを手元に用意してください)。

1
John Montgomery

他の答えはジェネリックの角度をカバーしています。 nil角度をカバーする簡単なコードを共有すると思います。

アプリに現在存在するすべてのLabelの静的配列(時々読む)が必要でしたが、以前のnilの場所は見たくありませんでした。

派手なものはありません、これは私のコードです...

public struct WeakLabel {
    public weak var label : Label?
    public init(_ label: Label?) {
        self.label = label
    }
}

public class Label : UILabel {
    static var _allLabels = [WeakLabel]()
    public static var allLabels:[WeakLabel] {
        get {
            _allLabels = _allLabels.filter{$0.label != nil}
            return _allLabels.filter{$0.label != nil}.map{$0.label!}
        }
    }
    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        Label._allLabels.append(WeakLabel(self))
    }
    public override init(frame: CGRect) {
        super.init(frame: frame)
        Label._allLabels.append(WeakLabel(self))
    }
}
1
wils

Arrayの周りにラッパーを作成できます。または、このライブラリを使用します https://github.com/NickRybalko/WeakPointerArraylet array = WeakPointerArray<AnyObject>()タイプセーフです。

0
Nick Rybalko

これは@Eonilの仕事に基づいています。クロージャーの弱バインド戦略が大好きだったのですが、変数に関数演算子を使用したくありませんでした。

代わりに、私がしたことは次のとおりです。

class Weak<T> where T: AnyObject {
    fileprivate var storedWeakReference: ()->T? = { return nil }

    var value: T? {
        get {
            return storedWeakReference()
        }
    }

    init(_ object: T) {
        self.storedWeakReference = storeWeakReference(object)
    }

    fileprivate func storeWeakReference<T> (_ target:T) -> ()->T? where T: AnyObject {
        return { [weak target] in
            return target
        }
    }
}

このようにして、次のようなことができます。

var a: UIViewController? = UIViewController()
let b = Weak(a)
print(a) //prints Optional(<UIViewController: 0xSomeAddress>)
print(b.value) //prints Optional(<UIViewController: 0xSomeAddress>)
a = nil
print(a) //prints nil
print(b.value) //prints nil
0
Ale Ravasio

これは、弱いオブジェクトのコンテナを保持するタイプセーフコレクションです。また、アクセス時にコンテナ/ラッパーを自動的に削除します。

例:

protocol SomeDelegate: class {
    func doSomething()
}

class SomeViewController: UIViewController {
    var delegates: WeakCollection<SomeDelegate> = []

    func someFunction(delegate: SomeDelegate) {
        delegates.append(delegate)
    }

    func runDelegates() {
        delegates.forEach { $0.doSomething() }
    }
}

カスタムコレクションhttps://Gist.github.com/djk12587/46d85017fb3fad6946046925f36cefdc

import Foundation

/**
 Creates an array of weak reference objects.
 - Important:
    Because this is an array of weak objects, the objects in the array can be removed at any time.
    The collection itself will handle removing nil objects (garbage collection) via the private function cleanUpNilContainers()
 */

class WeakCollection<T>: RangeReplaceableCollection, ExpressibleByArrayLiteral {
    typealias Index = Int
    typealias Element = T
    typealias Iterator = IndexingIterator<[Element]>

    private var weakContainers: [WeakReferenceContainer]

    required convenience init(arrayLiteral: Element...) {
        self.init()
        self.weakContainers = WeakCollection.createWeakContainers(from: arrayLiteral)
    }

    required init() {
        weakContainers = []
    }

    required init<S>(_ elements: S) where S: Sequence, WeakCollection.Element == S.Element {
        self.weakContainers = WeakCollection.createWeakContainers(from: elements)
    }

    static private func createWeakContainers<S>(from weakCollection: S) -> [WeakReferenceContainer] where S: Sequence,
        WeakCollection.Element == S.Element {
            return weakCollection.compactMap { WeakReferenceContainer(value: $0 as AnyObject) }
    }

    func append<S>(contentsOf newElements: S) where S: Sequence, WeakCollection.Element == S.Element {
        self.weakContainers.append(contentsOf: WeakCollection.createWeakContainers(from: newElements))
    }

    var startIndex: Index {
        return references.startIndex
    }

    var endIndex: Index {
        return references.endIndex
    }

    func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where
        C: Collection, R: RangeExpression, WeakCollection.Element == C.Element, WeakCollection.Index == R.Bound {
            weakContainers.replaceSubrange(subrange, with: WeakCollection.createWeakContainers(from: newElements))
    }

    func index(after i: Int) -> Int {
        return references.index(after: i)
    }

    func makeIterator() -> IndexingIterator<[Element]> {
        return references.makeIterator()
    }

    subscript(index: Int) -> Element {
        get {
            return references[index]
        }
        set {
            weakContainers[index] = WeakReferenceContainer(value: newValue as AnyObject)
        }
    }
}

extension WeakCollection {
    private class WeakReferenceContainer {
        private(set) weak var value: AnyObject?

        init(value: AnyObject?) {
            self.value = value
        }
    }

    private func cleanUpNilContainers() {
        weakContainers = weakContainers.compactMap { $0.value == nil ? nil : $0 }
    }

    private var references: [Element] {
        cleanUpNilContainers()
        return weakContainers.compactMap { $0.value as? T }
    }
}
0
Dan

この私のソリューション:

  • 割り当て解除時に配列をクリーンアップする。これは、WeakObjectSetが格納されており、WeakObjectを取得していないためです。
  • Setで要素が重複しているの場合の致命的なエラーを解決する

-

// MARK: - WeakObjectSet 

public class WeakObject<T: AnyObject>: Equatable, Hashable {

    // MARK: Public propreties

    public weak var object: T?
    public var hashValue: Int {
        return self.identifier.hashValue
    }

    // MARK: Private propreties

    private let identifier: ObjectIdentifier

    // MARK: Initializer

    public init(object: T) {
        self.identifier = ObjectIdentifier(object)
        self.object = object
    }

    public static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}

// MARK: - WeakObjectSet

public class WeakObjectSet<T: AnyObject> {

    // MARK: Public propreties

    public var allObjects: [T] {
        return allSetObjects.compactMap { $0.object }
    }

    // MARK: Private propreties

    private var objects: Set<WeakObject<T>>
    private var allSetObjects: Set<WeakObject<T>> {
        get {
            objects = self.objects.filter { $0.object != nil }
            return objects
        }
        set {
            objects.formUnion(newValue.filter { $0.object != nil })
        }
    }

    // MARK: Initializer

    public init() {
        self.objects = Set<WeakObject<T>>([])
    }

    public init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    // MARK: Public function

    public func contains(_ object: T) -> Bool {
        return self.allSetObjects.contains(WeakObject(object: object))
    }

    public func addObject(_ object: T) {
        self.allSetObjects.insert(WeakObject(object: object))
    }

    public func addObjects(_ objects: [T]) {
        objects.forEach { self.allSetObjects.insert(WeakObject(object: $0)) }
    }
}
0
YannSteph