代码之家  ›  专栏  ›  技术社区  ›  JimmyJammed

Swift-当调用者被释放时,如何正确地从数组中移除块?

  •  0
  • JimmyJammed  · 技术社区  · 7 年前

    我有一个“updateBlocks”数组 我在单例类中用于通知任何观察者 (UIViewController等)

    我想知道移除观察者的最佳方式是什么,这样当观察者被解除分配(或不再需要更新)时,它就不会被执行。

    这是我当前的设置:

    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 回复  |  直到 7 年前
        1
  •  1
  •   Duncan C    7 年前

    updateBlock updateBlocks 阵列设计不好。

    我建议去掉updateBlock var,而是定义一个 addUpdateBlock 方法和a 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()
        }
    

    如果将块保存在字典中,则它们不会按任何定义的顺序执行。

        2
  •  0
  •   rmaddy    7 年前

    由于您希望支持多个观察者,并提供删除观察者的能力,因此您确实需要 addBlock removeBlock 方法。那么API及其功能就清楚了。

    NotificationCenter 类,其中 添加块 移除块 方法

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