在我的一个类中,我使用了一个委托数组(该类是一个单例)。这导致保留周期。我知道当我只使用一个委托时,我可以避免保留周期。但这对我的代表阵列不起作用。
如何避免这种保留周期。
示例:
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 :(得分:3)
问题是weakDelegates
是一个强引用,它对WeakDelegateContainer类型元素的引用是一个强引用。
您的情况是班级NSHashTable存在的原因。使用weakObjects()
初始化。这将为您提供一组ARC弱引用,当引用的对象不存在时,每个引用都将被删除并被删除(您不需要任何额外的簿记,也不需要您的WeakDelegateContainer类型)。 / p>
您的集合必须输入为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。它的元素是指向void的指针,在Swift中可能有点冗长,但你只需要编写一次你的访问函数(归功于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()
答案 1 :(得分:1)
我在Using as a concrete type conforming to protocol AnyObject is not supported找到了解决方案。所有学分都归Kyle 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 ...
}