Swift - 如何在释放调用者时从数组中正确删除块?
Swift - How to properly remove blocks from an array when a caller is deallocated?
我有一个 'updateBlocks' (闭包) 的数组,我在单例中使用 class 来通知任何观察者 (UIViewControllers等) 数据更新时。
我想知道删除观察者的最佳方法是什么,以便在观察者被释放(或不再需要更新)时不执行观察者。
这是我当前的设置:
MySingletonClass
var updateBlock: (() -> ())? {
didSet {
self.updateBlocks.append(updateBlock!)
self.updateBlock!() // Call immediately to give initial data
}
}
var updateBlocks = [() -> ()]()
func executeUpdateBlocks() {
for block in updateBlocks {
block()
}
}
我的观察者Class
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。您可以将块的容器重构为字典并使用顺序整数键。当你添加一个新块时,你的 addBlock 函数可以 return key:
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()
}
如果您将块保存在字典中,那么它们将不会以任何定义的顺序执行。
这是一个非常令人困惑的问题 API。从客户的角度来看,您正在设置单个块的值。但是实现实际上将该块添加到一个数组中,然后立即调用该块。为什么要强制展开可选块?
既然你想支持多个观察者并提供移除观察者的能力,你真的在你的单例中显示了 addBlock
和 removeBlock
方法。那么API和它的功能就很清楚了。
诀窍是如何提供一个 API 让观察者告诉单例删除特定块。我将在 NotificationCenter
class 中如何完成 API 建模,其中 addBlock
方法 returns 一些生成的令牌。然后将该令牌传递给 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
我有一个 'updateBlocks' (闭包) 的数组,我在单例中使用 class 来通知任何观察者 (UIViewControllers等) 数据更新时。
我想知道删除观察者的最佳方法是什么,以便在观察者被释放(或不再需要更新)时不执行观察者。
这是我当前的设置:
MySingletonClass
var updateBlock: (() -> ())? {
didSet {
self.updateBlocks.append(updateBlock!)
self.updateBlock!() // Call immediately to give initial data
}
}
var updateBlocks = [() -> ()]()
func executeUpdateBlocks() {
for block in updateBlocks {
block()
}
}
我的观察者Class
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。您可以将块的容器重构为字典并使用顺序整数键。当你添加一个新块时,你的 addBlock 函数可以 return key:
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()
}
如果您将块保存在字典中,那么它们将不会以任何定义的顺序执行。
这是一个非常令人困惑的问题 API。从客户的角度来看,您正在设置单个块的值。但是实现实际上将该块添加到一个数组中,然后立即调用该块。为什么要强制展开可选块?
既然你想支持多个观察者并提供移除观察者的能力,你真的在你的单例中显示了 addBlock
和 removeBlock
方法。那么API和它的功能就很清楚了。
诀窍是如何提供一个 API 让观察者告诉单例删除特定块。我将在 NotificationCenter
class 中如何完成 API 建模,其中 addBlock
方法 returns 一些生成的令牌。然后将该令牌传递给 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