2017-09-10 82 views
0

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

回答

1

你单身的设计有一些问题。

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功能可能会返回键。

+0

是啊,我是想用不必使用添加/删除功能就完事了,但它是马虎不得。我结束了使用这样的事情,以帮助更好地跟踪呼叫者: func addUpdateBlock(_ block:@escaping() - >(),sender:AnyHashable){ updateBlocks [sender] = block } – JimmyJammed

0

这是一个非常令人困惑的API。从客户的角度来看,您正在设置单个块的值。但实现实际上将该块添加到数组,然后立即调用该块。为什么你会强制 - 解开可选块?

既然你想支持几个观察者,并提供删除观察者的能力,你真的在​​你的单例中显示有addBlockremoveBlock方法。那么API和它的功能是清楚的。

诀窍是如何提供一个API,让观察者告诉单例删除特定的块。我将在NotificationCenter类中的方法完成后对API进行建模,其中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 
+0

Thanks!我最终用这个代替了: func addUpdateBlock(_ block:@escaping() - >(),sender:AnyHashable){ updateBlocks [sender] = block } – JimmyJammed