2017-02-25 10 views
0

私のクラスの1つでは、デリゲートの配列を使用しています(クラスはシングルトンです)。これは保持サイクルを引き起こしています。デリゲートを弱くしてデリゲートを1つだけ使用すると、保持サイクルを回避できます。しかし、これは私の代理人の配列では機能しません。Swiftでデリゲートの配列を使用するときの保持サイクルを回避する方法

どうすればこの保持サイクルを回避できますか。

例:

protocol SomeDelegate: class { 
    func someFunction() 
} 

私のクラス

class SomeClass { 
    // This class is a singleton! 
    static let sharedInstance = SomeClass() 

    var delegates = [SomeDelegate]() // this is causing a retain cycle 
    weak var delegate: SomeDelegate? // this is ok. 

    ... other code... 
} 
+0

答えが準拠するクラスを参照しますAnyObjectへ。私はAnyObjectに従わないプロトコルオブジェクトの配列を持っています。 – Leontien

+0

プロトコルは自分自身に準拠することができないので、リンクされた答えをバットからすぐに使うことはできません( 'AnyObject'制約を' SomeDelegate'に置き換えると思うかもしれません)にリンクする。しかし、あなたは[次のQ&A](http://stackoverflow.com/questions/32807948/using-as-a-concrete-type-conforming-to-protocol-anyobject-is-not-supported)を見ることができます。 'class'制約付きプロトコルインスタンスを弱参照で保持できるカスタムコンテナを実装しています。 – dfri

+0

dfriのリンクが助けになりました。私は一般的な解決策が必要ないので、Kyle Redfearnの答えを使用しました。私はこの質問の答えに私の解決策を置くので、他人のために見つけるのが簡単になります。 – Leontien

答えて

1

問題はweakDelegatesが強い参照と型WeakDelegateContainerのその要素への参照であることであるが、強い参照です。

あなたの状況は、クラスNSHashTableが存在する理由です。 weakObjects()を使用して初期化します。これはARC弱参照のセットを提供します。それぞれの参照は、参照オブジェクトが存在しなくなったときには削除され、削除されます(WeakDelegateContainerタイプは必要ありません)。

あなたのセットがANYOBJECTを保持しているとして入力する必要がありますが、あなたは簡単にあなたが供給しSomeDelegate準拠のオブジェクトを取得していることを保証するために媒介することができる:

let list = NSHashTable<AnyObject>.weakObjects() 
func addToList(_ obj:SomeDelegate) { 
    list.add(obj) 
} 
func retrieveFromList(_ obj:SomeDelegate) -> SomeDelegate? { 
    if let result = list.member(obj) as? SomeDelegate { 
     return result 
    } 
    return nil 
} 
func retrieveAllFromList() -> [SomeDelegate] { 
    return list.allObjects as! [SomeDelegate] 
} 

retrieveAllFromList()リストはまだ存在してオブジェクトのみ機能。存在していたオブジェクトは、NSHashTableのnilに変更され、allObjectsには含まれていません。それは私が "余分な簿記なし"という意味です。 NSHashTableはすでに簿記を行っています。

func test() { 
    let c = SomeClass() // adopter of SomeDelegate 
    self.addToList(c) 
    if let cc = self.retrieveFromList(c) { 
     cc.someFunction() 
    } 
    print(self.retrieveAllFromList()) // one SomeClass object 
    delay(1) { 
     print(self.retrieveAllFromList()) // empty 
    } 
} 

また、あなたがNSPointerArrayを使用することができます。ここでは

はそれをテストするコードです。その要素は、スウィフトに使うには少し冗長になり得る、ポインタ・ツー・空ですが、あなただけ(https://stackoverflow.com/a/33310021/341994にクレジット)一度あなたのアクセサ関数を記述する必要があります。ここでは

let parr = NSPointerArray.weakObjects() 
func addToArray(_ obj:SomeDelegate) { 
    let ptr = Unmanaged<AnyObject>.passUnretained(obj).toOpaque() 
    self.parr.addPointer(ptr) 
} 
func fetchFromArray(at ix:Int) -> SomeDelegate? { 
    if let ptr = self.parr.pointer(at:ix) { 
     let obj = Unmanaged<AnyObject>.fromOpaque(ptr).takeUnretainedValue() 
     if let del = obj as? SomeDelegate { 
      return del 
     } 
    } 
    return nil 
} 

はそれをテストするためのコードです:

let c = SomeClass() 
    self.addToArray(c) 
    for ix in 0..<self.parr.count { 
     if let del = self.fetchFromArray(at:ix) { 
      del.someFunction() // called 
     } 
    } 
    delay(1) { 
     print(self.parr.count) // 1 
     for ix in 0..<self.parr.count { 
      if let del = self.fetchFromArray(at:ix) { 
       del.someFunction() // not called 
      } 
     } 
    } 

興味深いことに、私たち工assが存在の外に出た後、私たちの配列のcountは1のままである - しかし、someFunctionを呼び出すために、それを循環し、someFunctionへの呼び出しはありません。これは、配列内のSomeClassポインタがnilに置き換えられたためです。 NSHashTableとは異なり、配列は自動的に要素nilのパージされません。彼らは、私たちのアクセサコードがエラーに対して守られているので、何の害もしませんが、あなたは、配列を圧縮したい場合は、ここでは(https://stackoverflow.com/a/40274426/341994)それを行うためのトリックです:その質問の

self.parr.addPointer(nil) 
    self.parr.compact() 
+0

私はこれを試しました。私は 'var test = NSHashTable .weakObjects()'を宣言すると 'SomeDelegateをプロトコルに準拠した具体的な型として使用しています。AnyObjectはサポートされていません'私は問題は、ほとんどのソリューションはプロトコルのために動作しないと思う。 WeakDelegateContainerでの指定された応答は、プロトコルに対して機能します。この場合も参照は除外されますが、nilified参照のために配列を整理します。 – Leontien

+0

良い応答。はい、私はコメントで指摘と同じトラップに落ちた、私はしませんでしたか? – matt

+0

NSPointerArrayは新しいものです。私が理解しているように、私は弱いオブジェクトの配列に追加するUnsafeMutableRawPointerが必要です。私は、デリゲートオブジェクトに対してUnsafeMutableRawPointerをどのように取得するのかを理解することができません。 – Leontien

1

私はUsing as a concrete type conforming to protocol AnyObject is not supportedで解決策を見つけました。カイル・レッドファーンのすべてのクレジット

マイ溶液

protocol SomeDelegate: class { 
    func someFunction() 
} 

class WeakDelegateContainer : AnyObject { 
    weak var weakDelegate: SomeDelegate? 
} 

class SomeClass { 
    // This class is a singleton! 
    static let sharedInstance = SomeClass() 

    fileprivate var weakDelegates = [WeakDelegateContainer]() 

    func addDelegate(_ newDelegate: SomeDelegate) { 
     let container = WeakDelegateContainer() 
     container.weakDelegate = newDelegate 
     weakDelegates.append(container) 
    } 

    func removeDelegate(_ delegateToRemove: SomeDelegate) { 
     // In my case: SomeDelegate will always be of the type UIViewController 
     if let vcDelegateToRemove = delegateToRemove as? UIViewController { 
      for i in (0...weakDelegates.count - 1).reversed() { 
       if weakDelegates[i].weakDelegate == nil { 
        // object that is referenced no longer exists 
        weakDelegates.remove(at: i) 
        continue 
       } 

       if let vcDelegate = weakDelegates[i].weakDelegate as? UIViewController { 
        if vcDelegate === vcDelegateToRemove { 
         weakDelegates.remove(at: i) 
        } 
       } 
      } 
     } 
    } 

    ... other code ... 
} 
関連する問題