Swift - 如何在释放调用者时正确地从数组中删除块?

时间:2017-09-10 02:20:35

标签: ios swift swift3 closures observers

我有一个'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???

2 个答案:

答案 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。从客户端的角度来看,您正在设置单个块的值。但实现实际上会将该块添加到数组中,然后立即调用该块。为什么要强行拆开可选块?

由于您希望支持多个观察者并提供删除观察者的功能,因此您的单例中确实显示了addBlockremoveBlock方法。然后API及其功能很明确。

技巧是如何提供一个API,让观察者告诉单例删除特定的块。我将在NotificationCenter类中完成如何完成后对addBlock方法返回一些生成的令牌进行建模。然后该标记将传递给removeBlock方法。

实现可能是在令牌上键入的字典,值是块。令牌可以是UUID或其他一些生成的唯一不透明值。这使得addBlockremoveBlock方法变得简单。然后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