我有一个'updateBlocks'(closures)数组,我在单例类中使用它来在数据更新时通知任何观察者(UIViewControllers等)。
我想知道删除观察者的最佳方法是什么,以便在取消分配观察者(或不再需要更新)时不执行它。
这是我目前的设置:
MySingleton Class
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???
答案 0 :(得分:1)
你的单身人士设计存在一些问题。
让updateBlock
成为变量,其中didSet方法会将一个块附加到updateBlocks
数组,这是糟糕的设计。
我建议删除updateBlock var,而是定义addUpdateBlock
方法和removeAllUpdateBlocks
方法:
func addUpdateBlock(_ block () -> ()) {
updateBlocks.append(block)
}
func removeAllUpdateBlocks() {
updateBlocks.removeAll()
}
func executeUpdateBlocks() {
for block in updateBlocks {
block()
}
}
如果你想删除单个块,那么你需要一些方法来跟踪它们。正如rmaddy所说,你需要为每个区块提供某种ID。您可以将容器的重构重构为字典并使用顺序整数键。添加新块时,addBlock函数可以返回键:
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()
}
如果您将块保存在字典中,那么它们将无法以任何已定义的顺序执行。
答案 1 :(得分:0)
这是一个非常令人困惑的API。从客户端的角度来看,您正在设置单个块的值。但实现实际上会将该块添加到数组中,然后立即调用该块。为什么要强行拆开可选块?
由于您希望支持多个观察者并提供删除观察者的功能,因此您的单例中确实显示了addBlock
和removeBlock
方法。然后API及其功能很明确。
技巧是如何提供一个API,让观察者告诉单例删除特定的块。我将在NotificationCenter
类中完成如何完成后对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