在 Swift 中使用委托数组时如何避免保留循环
How to avoid a retain cycle when using an array of delegates in Swift
在我的一个 classes 中,我使用了一组委托(class 是一个单例)。这会导致保留周期。我知道当我只使用一个委托时,我可以通过弱化委托来避免保留周期。但这对我的代表阵列不起作用。
我怎样才能避免这个保留周期。
示例:
protocol SomeDelegate: class {
func someFunction()
}
我的Class
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...
}
我在 中找到了解决方案。所有学分归功于 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 ...
}
问题在于 weakDelegates
是一个强引用,它对其类型为 WeakDelegateContainer 的元素的引用是一个强引用。
你的情况就是 class 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
中。这就是我所说的 "no extra bookkeeping"; 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。它的元素是指向空的指针,在 Swift 中使用可能有点冗长,但您只需编写一次访问器函数(归功于 ):
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
元素。它们没有害处,因为我们的访问器代码已防止错误,但如果您想压缩数组,这里有一个技巧 ():
self.parr.addPointer(nil)
self.parr.compact()
在我的一个 classes 中,我使用了一组委托(class 是一个单例)。这会导致保留周期。我知道当我只使用一个委托时,我可以通过弱化委托来避免保留周期。但这对我的代表阵列不起作用。
我怎样才能避免这个保留周期。
示例:
protocol SomeDelegate: class {
func someFunction()
}
我的Class
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...
}
我在
我的解决方案
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 ...
}
问题在于 weakDelegates
是一个强引用,它对其类型为 WeakDelegateContainer 的元素的引用是一个强引用。
你的情况就是 class 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
中。这就是我所说的 "no extra bookkeeping"; 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。它的元素是指向空的指针,在 Swift 中使用可能有点冗长,但您只需编写一次访问器函数(归功于
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
元素。它们没有害处,因为我们的访问器代码已防止错误,但如果您想压缩数组,这里有一个技巧 (
self.parr.addPointer(nil)
self.parr.compact()