问题在于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。它的元素是指向 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()