Swift - 当调用者被释放时如何从数组中正确地移除块?
我有一个'updateBlocks'(闭包),我在一个单例类中通知任何观察者(UIViewControllers等)当数据更新。Swift - 当调用者被释放时如何从数组中正确地移除块?
我想知道什么最好的方式来删除观察员将是当观察员被解除分配(或不再需要更新)时,它不会被执行。
这里是我的当前设置:
MySingleton类
var updateBlock: (() ->())? {
didSet {
self.updateBlocks.append(updateBlock!)
self.updateBlock!() // Call immediately to give initial data
}
}
var updateBlocks = [() ->()]()
func executeUpdateBlocks() {
for block in updateBlocks {
block()
}
}
MyObserver类
MySingleton.shared.updateBlock = {
...handle updated data...
}
MySingleton.shared.updateBlock = nil // How to properly remove???
你单身的设计有一些问题。
有updateBlock
是一个变量谁的didSet方法附加块到您的updateBlocks
数组是坏的设计。
我建议摆脱updateBlock变种,而是定义addUpdateBlock
方法和removeAllUpdateBlocks
方法:
func addUpdateBlock(_ block() ->()) {
updateBlocks.append(block)
}
func removeAllUpdateBlocks() {
updateBlocks.removeAll()
}
func executeUpdateBlocks() {
for block in updateBlocks {
block()
}
}
如果你想删除单个块,那么你就需要一些跟踪他们的方式。正如rmaddy所说的,你需要为每个区块设置一些ID。您可以将您的容器重构为字典,并使用顺序整数键。如果您保存块一本字典那么他们将不会在任何定义的顺序执行
var updateBlocks = [Int:() ->()]()
var nextBlockID: Int = 0
func addUpdateBlock(_ block() ->()) -> Int {
updateBlocks[nextBlockID] = block
let result = nextBlockID
nextBlockID += 1
//Return the block ID of the newly added block
return result
}
func removeAllUpdateBlocks() {
updateBlocks.removeAll()
}
func removeBlock(id: Int) -> Bool {
if updateBlocks[id] == nil {
return false
} else {
updateBlocks[id] = nil
return true
}
func executeUpdateBlocks() {
for (_, block) in updateBlocks {
block()
}
:当您添加一个新的模块,您addBlock功能可能会返回键。
这是一个非常令人困惑的API。从客户的角度来看,您正在设置单个块的值。但实现实际上将该块添加到数组,然后立即调用该块。为什么你会强制 - 解开可选块?
既然你想支持几个观察者,并提供删除观察者的能力,你真的在你的单例中显示有addBlock
和removeBlock
方法。那么API和它的功能是清楚的。
诀窍是如何提供一个API,让观察者告诉单例删除特定的块。我将在NotificationCenter
类中的方法完成后对API进行建模,其中addBlock
方法返回一些生成的令牌。该令牌然后传递给removeBlock
方法。
该实现可能是一个字典在标记上键入,值是该块。令牌可以是UUID
或其他生成的唯一不透明值。这使得addBlock
和removeBlock
方法变得简单。然后executeBlocks
方法会迭代字典的值并调用这些块。
这里是一个可能实现:
class UpdateBlocks {
static let shared = UpdateBlocks()
var blocks = [UUID:() ->()]()
private init() {
}
func addBlock(_ block: @escaping() ->()) -> Any {
let token = UUID()
blocks[token] = block
return token
}
func removeBlock(_ token: Any) {
if let token = token as? UUID {
blocks[token] = nil
}
}
func executeBlocks() {
for (_, value) in blocks {
value()
}
}
}
let token = UpdateBlocks.shared.addBlock {
print("hello")
}
UpdateBlocks.shared.executeBlocks() // Outputs "hello"
UpdateBlocks.shared.removeBlock(token)
UpdateBlocks.shared.executeBlocks() // No output
Thanks!我最终用这个代替了: func addUpdateBlock(_ block:@escaping() - >(),sender:AnyHashable){ updateBlocks [sender] = block } – JimmyJammed
是啊,我是想用不必使用添加/删除功能就完事了,但它是马虎不得。我结束了使用这样的事情,以帮助更好地跟踪呼叫者: func addUpdateBlock(_ block:@escaping() - >(),sender:AnyHashable){ updateBlocks [sender] = block } – JimmyJammed