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


179

弱参照の配列をSwiftに保存したいのですが。配列自体は弱参照であってはなりません-その要素はそうでなければなりません。Cocoa NSPointerArrayはこれのタイプセーフでないバージョンを提供していると思います。


1
別のオブジェクトを弱く参照するコンテナオブジェクトを作成してから、それらの配列を作成するのはどうですか?(より適切な回答が得られない場合)
nielsbot 14年

1
NSPointerArrayを使用しないのはなぜですか?
Bastian

@nielsbotそれは古いobj-cソリューションです:) Swiftyにするためには、汎用オブジェクトである必要があります!:)しかし、実際の問題は、参照されるオブジェクトの割り当てが解除されたときに、オブジェクトを配列から削除する方法です。
スルタン、2014年

2
そうです、パラメーター化された型の何かを好みます。NSPointerArrayの周りにパラメーター化されたラッパーを作成できると思いますが、他に選択肢があるかどうかを確認したいと思いました。
ビル・

6
別のオプションと同様に、NSHashTableが存在します。これは基本的にNSSetであり、含まれるオブジェクトを参照する方法を指定できます。
Mick MacCallum、2014年

回答:


154

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

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言語がそのように定義された拡張を許可するとは思いません。


11
値の割り当てが解除されたときに、配列からラッパーオブジェクトをどのように削除しますか?
スルタン

9
はい、コンパイラがクラッシュしました。
GoZoner 2014年

5
新しい質問に問題コードを投稿してください。それあなたのコードであるかもしれないときに私の答えを口にする理由はありません!
GoZoner 2015

2
@EdGamble提供されたコードはそのまま動作しますが、クラスStuffをプロトコルで置き換えると失敗します。この関連質問を
Theo

2
構造体は、ヒープフェッチを必要とする代わりにスタックに保持されるため、より優れています。
KPM

60

NSHashTableをweakObjectsHashTableと共に使用できます。 NSHashTable<ObjectType>.weakObjectsHashTable()

Swift 3の場合: NSHashTable<ObjectType>.weakObjects()

NSHashTableクラスリファレンス

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

iOS 6.0以降で利用できます。


ベストアンサーで、ラッパーの時間を無駄にしないでください!
Ramis

1
これは賢い方法ですが、GoZonerの回答と同様に、プロトコルなどAny、そうAnyObjectではないタイプでは機能しません。
Aaron Brager、2016年

@SteveWilfordしかし、プロトコルはクラスによって実装できるため、参照型になります
Aaron Brager

4
プロトコルはクラスを拡張でき、それを弱いものとして使用できます(例:プロトコルMyProtocol:クラス)
Yasmin Tiomkin

1
MyProtocol: classおよびでコンパイラエラーが発生しNSHashTable<MyProtocol>.weakObjects()ます。「『NSHashTableは』 『MyProtocol』はクラス型である必要があります。
グレッグ

14

パーティーにはちょっと遅れますが、私のものを試してください。配列ではなくセットとして実装しました。

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ではないからです。私の迅速なバージョンは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の動作の違いはこのコンテキストを超えていますが、誰かがこの問題を理解してくれれば幸いです。


このソリューションを自分のプロジェクトに採用しました。よくやった!ほんの1つの提案として、このソリューションはnil内部から値を削除しないようですSet。だから私はreap()上の答えで言及された関数を追加し、アクセスさreap()れるたびに必ず呼び出すようにしましたWeakObjectSet
gokeji

うーん待ち時間は、これがスウィフト4で動作しません何らかの理由で/ iOS搭載11.値になると弱参照がすぐに割り当てが解除されないように思えるnilもう
gokeji

1
コードをSwift4に更新しました。答えの後半を参照してください。NSStringには割り当て解除の問題があるようですが、それでもカスタムクラスオブジェクトで機能するはずです。
吉川カズ

@KazYoshikawaを調べて回答を更新していただき、ありがとうございます。また、後でカスタムクラスが機能するのにNSString機能しないことにも気付きました。
gokeji

2
によって返されるポインターがUnsafeMutablePointer<T>(&object)ランダムに変化する可能性があるという経験をしました(と同じwithUnsafePointer)。私は今に裏打ちされたバージョンを使用NSHashTablegist.github.com/simonseyer/cf73e733355501405982042f760d2a7dを
simonseyer

12

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

@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]

1
奇妙ですが、構造体では動作しません。言うEXC_BAD_ACCESS私のため。クラスがうまく機能する
2015年

6
構造体は値の型であり、機能しません。コンパイル時エラーではなく実行時にクラッシュしたという事実は、コンパイラのバグです。
David Goodine 16

10

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

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

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

var weakThings = WeakThing<Foo>[]()

変数classを使用するにはaでなければなりませんweak
Bill

3
誰が言ったのですか?上記のコードは私にとってはうまくいきます。唯一の要件は、弱くなっているオブジェクトがクラスである必要があり、弱参照を保持しているオブジェクトではないことです
Joshua Weinberg

ごめんなさい。「構造体では弱い変数を使用できません」というコンパイラメッセージが表示されただけかもしれません。あなたは正しいです-それはコンパイルされます。
ビル・

5
@JoshuaWeinberg Fooがプロトコルの場合はどうなりますか?
onmyway133 2015

@ onmyway133 AFAIKプロトコルが機能するクラスによってのみ実装されると宣言されている場合。 protocol Protocol : class { ... }
olejnjak

8

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

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() }))

実際には、クロージャーを作成するための関数は必要ありません。オブジェクトを直接キャプチャするだけです。

let captured3 = { [weak obj3] in return obj3 }

3
問題は、弱いオブジェクトの配列(またはSetなど)を作成する方法です。
David H

このソリューションでは、のような複数の値を持つ配列を作成することもできますvar array: [(x: Int, y: () -> T?)]。まさに、私が探していたもの。
jboi 2016

1
@DavidH私は質問に答えるために私の答えを更新しました。これがお役に立てば幸いです。
eonil 2016

このアプローチが大好きで、とても賢いと思います。この戦略を使用してクラスの実装を作成しました。ありがとうございました!
エールRavasio

についてはあまりわかりませんlet values = Array(array1.map({ $0() })) part。これは弱い参照を持つクロージャーの配列ではなくなったため、この配列が割り当て解除されるまで値は保持されます。私が正しければself.items = Array(array1.map({ $0() }))、目的を達成するためにこの配列を保持してはならないことに注意することが重要です。
マティックオブラック

7

ジェネリックでウィークコンテナーを作成することも同じ考えでした。
その結果、私はラッパーを作成しました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)。
そのためAnyObjectfatalError()アサーションに準拠することをやめ、ガードを追加しました。



6

細部

  • Swift 5.1、Xcode 11.3.1

解決

struct WeakObject<Object: AnyObject> { weak var object: Object? }

オプション1

@propertyWrapper
struct WeakElements<Collect, Element> where Collect: RangeReplaceableCollection, Collect.Element == Optional<Element>, Element: AnyObject {
    private var weakObjects = [WeakObject<Element>]()

    init(wrappedValue value: Collect) { save(collection: value) }

    private mutating func save(collection: Collect) {
        weakObjects = collection.map { WeakObject(object: $0) }
    }

    var wrappedValue: Collect {
        get { Collect(weakObjects.map { $0.object }) }
        set (newValues) { save(collection: newValues) }
    }
}

オプション1の使用法

class Class1 { // or struct
    @WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

オプション2

struct WeakObjectsArray<Object> where Object: AnyObject {
    private var weakObjects = [WeakObject<Object>]()
}

extension WeakObjectsArray {
    typealias SubSequence = WeakObjectsArray<Object>
    typealias Element = Optional<Object>
    typealias Index = Int
    var startIndex: Index { weakObjects.startIndex }
    var endIndex: Index { weakObjects.endIndex }
    func index(after i: Index) -> Index { weakObjects.index(after: i) }
    subscript(position: Index) -> Element {
        get { weakObjects[position].object }
        set (newValue) { weakObjects[position] = WeakObject(object: newValue) }
    }
    var count: Int { return weakObjects.count }
    var isEmpty: Bool { return weakObjects.isEmpty }
}

extension WeakObjectsArray: RangeReplaceableCollection {
    mutating func replaceSubrange<C : Collection>( _ subrange: Range<Index>, with newElements: C) where Element == C.Element {
        weakObjects.replaceSubrange(subrange, with: newElements.map { WeakObject(object: $0) })
    }
}

オプション2の使用法

class Class2 { // or struct
    var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

完全なサンプル

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

import UIKit

class ViewController: UIViewController {

    @WeakElements var weakObjectsArray = [UIView?]()
    //var weakObjectsArray = WeakObjectsArray<UIView>()

    override func viewDidLoad() {
        super.viewDidLoad()
        addSubviews()
    }

    private func printArray(title: String) {
        DispatchQueue.main.async {
            print("=============================\n\(title)\ncount: \(self.weakObjectsArray.count)")
            self.weakObjectsArray.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() {
        (0...1).forEach { _ in addView() }
        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.first { view -> Bool in return !(view is UIButton) }?
            .removeFromSuperview()

        printArray(title: "First view deleted")
    }

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

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

両方のオプション(および他の多くのオプション)に関する私の問題は、これらのタイプのアレイがプロトコルで使用できないことです。例えば、これはコンパイルされません:protocol TP: class { } class TC { var a = WeakArray<TP>() var b = WeakObjectsArray<TP>() }
MaticはOblak

@MaticOblakジェネリックの使用についてはどうですか? protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
Vasily Bodnarchuk

この配列は、同じクラスプロトコルを実装するさまざまなタイプのオブジェクトをこの配列に保持できるという考え方です。ジェネリックを使用することにより、それを単一のタイプにロックダウンします。たとえば、のような配列を保持するシングルトンがあると想像してくださいdelegates。次に、この機能を使用するビューコントローラーがいくつかあります。電話をかけることを期待するでしょうMyManager.delegates.append(self)。しかし、MyManagerあるジェネリック型にロックされている場合、これはあまり使用できません。
マティックオブラック

@MaticOblakわかりました。これを試してください: protocol TP: class { } class MyManager { typealias Delegate = AnyObject & TP static var delegates = [Delegate?]() } class A: TP { } class B: TP { } //MyManager.delegates.append(A()) //MyManager.delegates.append(B())
Vasily Bodnarchuk

これで、配列で一般的な部分が失われましたが、これは少し重要です:)これは実行できないと感じています。現時点でのSwiftの制限...
マティックオブラック

4

WeakContainerの既存の例は役に立ちますが、リストや辞書などの既存の迅速なコンテナーで弱参照を使用するのには役立ちません。

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

ここで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
}

3

これの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私は手持ちの正確なコードを持っていないので)。


2

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

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

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

他の答えはジェネリックスの角度をカバーしています。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))
    }
}

&のflatMap代わりに使用するのはどうですか?filtermap
Lukas Kubanek 2015年

0

私はこれを@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

これは私の解決策です:

  • 割り当て解除時に配列をクリーンアップします。これは、WeakObjectSetが保存され、WeakObjectから取得されないためです
  • セットで重複する要素が見つかった場合の致命的なエラーを解決する

-

// 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

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

例:

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

何についての機能的なアプローチ

let observers = [() -> Observer?]()

observers.append({ [weak anObserver] in return anObserver })

これが主なアイデアです。次に、便利なロジックを追加して、配列の内容を追跡します。たとえば、そこに何があるかを見つける方法として、キーを使用して辞書で同じアプローチを考えることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.