2017-02-25 76 views
0

在我的一个类中,我使用了一个委托数组(该类是一个单例)。这导致了一个保留周期。我知道当我只使用一个代表时,我可以避免保留周期。但是这不适用于我的代表阵列。如何避免在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

协议无法符合自己的要求,因此当使用协议本身作为你想要容纳的弱包装对象时,链接的答案不能直接用于bat(我们可能会想'用'SomeDelegate'替换'AnyObject'约束)链接到。但是,您可以查看[以下问答](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-weak引用,当引用的对象不存在时(不需要任何额外的簿记,也不需要你的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 
      } 
     } 
    } 

有趣的是,我们的SomeClass的熄灭的存在后,我们的磁盘阵列的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()',但是当我声明'var test = NSHashTable .weakObjects()'时,我得到错误'使用SomeDelegate作为符合协议AnyObject的具体类型不被支持”。我认为问题在于大多数解决方案不适用于协议。 WeakDelegateContainer给出的答案适用于协议。在这种情况下,引用也会被忽略,但是我会清理我的数组以获取被忽略的引用。 – Leontien

+0

回复良好。是的,我陷入了评论中指出的陷阱,是不是? – matt

+0

NSPointerArray对我来说是新的。据我了解,我需要一个UnsafeMutableRawPointer添加到弱对象数组。我无法理解如何将UnsafeMutableRawPointer传递给我的委托对象。 – Leontien

1

我发现溶液中Using as a concrete type conforming to protocol AnyObject is not supported。所有学分凯尔Redfearn。

我的解决办法

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 ... 
} 
相关问题