Arrays 在数组中保持和释放闭包

Arrays 在数组中保持和释放闭包,arrays,swift,closures,Arrays,Swift,Closures,我希望在不依赖反应性第三方库/框架的情况下创建一个可观察的属性 我读了这篇文章,并对它们的可观测属性给出了一个类似的解决方案 他们的 class Observable<T> { let didChange = Event<(T, T)>() private var value: T init(_ initialValue: T) { value = initialValue } func set(newValue: T) { l

我希望在不依赖反应性第三方库/框架的情况下创建一个可观察的属性

我读了这篇文章,并对它们的可观测属性给出了一个类似的解决方案

他们的

class Observable<T> {

  let didChange = Event<(T, T)>()
  private var value: T

  init(_ initialValue: T) {
    value = initialValue
  }

  func set(newValue: T) {
    let oldValue = value
    value = newValue
    didChange.raise(oldValue, newValue)
  }

  func get() -> T {
    return value
  }
}
可观察的类{
让didChange=Event()
私有var值:T
初始值(u初始值:T){
值=初始值
}
func集(新值:T){
让oldValue=value
值=新值
didChange.raise(旧值、新值)
}
func get()->T{
返回值
}
}
地雷

可观察的公共类{
公共变量值:V{didSet{for observer in observer{observer(value)}}
私有var观察员=[(V)->Void]()
公共初始化(uinital:V){
值=初始值
}
公共函数观察(带闭包:@escaping(V)->Void){
观察员:追加(结束)
}
}
唯一的区别是我想捕获一个闭包数组,而不是使用
Event
addHander
。。。原因是我想提供传递值的语法,而不是让代码的使用者每次都做一个函数,不依赖任何第三方代码

我不确定一旦释放了这些闭包的所有者,它们是如何自动从阵列中删除的。我猜他们不能,这就是为什么使用了
addHandler
,我只是希望有人比我更了解这个问题


感谢您的时间。

最简单、可能也是最安全的解决方案是使用您所拥有的确切实现,但在执行任何操作/副作用之前,请确保所有调用者使用
[弱自我]
并确保
self
仍然存在

这样,当执行闭包数组时,任何创建者已经dealloc的闭包都将在调用时立即返回

// called from outside class
observer.observe { [weak self] in 
    guard strongSelf = self else { return }

    // do work using `strongSelf`
}
如果观察器将被许多不断释放的实例使用,我建议添加一个removeobserver函数。为此,您可能需要在
observe
调用中返回一个字符串,然后使用它删除闭包。大致如下:

public typealias ObserverIdentifier = String
public class Observable<V> {
    public var value: V { didSet { for observer in observers.values { observer(value) } }}
    private var observers = [ObserverIdentifier  : (V) -> Void]()

    public init(_ initital: V) {
        value = initital
    }

    @discardableResult public func observe(with closure: @escaping (V) -> Void) -> ObserverIdentifier {
        let identifier = UUID().uuidString
        observers[identifier] = closure
        return identifier
    }

    public func remove(identifier: ObserverIdentifier) {
        observers.removeValue(forKey: identifier)
    }
}
public-typealias-observer-identifier=String
可观测的公共类{
公共变量值:V{didSet{for observer in observer.values{observer(value)}}}
私有var观察员=[ObserverIdentifier:(V)->Void]()
公共初始化(uinital:V){
值=初始值
}
@discardableResult public func observe(带闭包:@escaping(V)->Void)->observer标识符{
让标识符=UUID().uuiString
观察者[标识符]=闭包
返回标识符
}
公共函数删除(标识符:ObserverIdentifier){
.removeValue(forKey:identifier)
}
}

因为您使用的是
[weak self]
,在解除分配时删除观察者是一件很好的事情,可以避免一些额外的无操作,但如果不删除,仍然是完全安全的。

因此我提出了以下解决方案:

类包装器{
var观察者:(V)->Void
公共初始化(b:@escaping(V)->Void){
观察者=b
}
}
类可观测{
公共变量值:V{didSet{
让枚举器=观察者。objectEnumerator()
当let wrapper=enumerator?.nextObject()时{
(包装器为!包装器).observer(值)
}
}}
私有var Observators=NSMapTable(键选项:[.weakMemory],值选项:[.strongMemory])
公共初始化(uinital:V){
值=初始值
}
public func observe(u订户:AnyObject,带闭包:@escaping(V)->Void){
let wrapper=wrapper(闭包)
setObject(包装器,forKey:subscriber)
}
}
最终API要求订户在调用时识别自己:

Observable.observe(self /* <-- extra param */) { /* closure */ }

OK so@hackape用
objc\u setassociated对象回答

public class Observable<V> {

    private class ClosureWrapper<V> {
        var closure: (V) -> Void
        public init(_ closure: @escaping (V) -> Void) {
            self.closure = closure
        }
    }

    private var observers = NSMapTable<AnyObject, ClosureWrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.weakMemory])
    public var value: V { didSet { notify() } }

    public init(_ initital: V) {
        value = initital
    }

    public func addObserver(_ object: AnyObject, skipFirst: Bool = true, closure: @escaping (V) -> Void) {
        let wrapper = ClosureWrapper(closure)
        let reference = "observer\(UUID().uuidString)".replacingOccurrences(of: "-", with: "")
        observers.setObject(wrapper, forKey: object)

        // Giving the closure back to the object that is observing
        // allows ClosureWrapper to die at the same time as observing object
        objc_setAssociatedObject(object, reference, wrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        if !skipFirst { closure(value) }
    }

    private func notify() {
        let enumerator = observers.objectEnumerator()
        while let wrapper = enumerator?.nextObject() { (wrapper as? ClosureWrapper<V>)?.closure(value) }
    }
}
可观察的公共类{
私有类ClosureWrapper{
var闭包:(V)->Void
public init(u-closure:@escaping(V)->Void){
self.closure=闭包
}
}

private var Observators=NSMapTable

FYI所以关键的区别是你的impl接受一个闭包,而事件版本接受一个函数?你为什么不使用notification center,它做完全相同的事情?顺便说一句,notification center使用的技巧是返回一个对象,该对象将在解除分配时删除观察者。这意味着当你紧紧抓住那个对象时,观察者就会存在。@hackape他们还使用一些第三方框架
事件
KVO@Sulthan这是一个好主意…让我更新字典而不是数组是第一步。我不确定你是否应该使用
discardableResult
,因为这意味着观察者永远无法删除。我更喜欢NotificationCenter使用的解决方案,它会自动删除观察者,除非你保持返回值。Casey我担心我API的消费者…是的,他们应该使用弱self,但我想在所有者死后放弃关闭…我希望负担在我而不是我的消费者身上。@Sulthan我也在想…我将用一个通知更新我的问题,例如,如果代码是为消费者编写的,那么肯定没有
@discardableResult
!在内部,我发现它非常有用,使用起来非常安全。作为记录,我认为API一点也不坏,但在我的实现中,我改变了措辞,即o
public func addObserver(object:AnyObject,closure:@escaping(V)->Void){
(您可以将弱引用保留为1,然后将其激活。根据我的示例编辑您的代码片段,应该很容易在viewController中重新生成。)project@Magoo我以后会调查的。干杯,伙计,我真的很高兴事情的结果。
public class Observable<V> {

    private class ClosureWrapper<V> {
        var closure: (V) -> Void
        public init(_ closure: @escaping (V) -> Void) {
            self.closure = closure
        }
    }

    private var observers = NSMapTable<AnyObject, ClosureWrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.weakMemory])
    public var value: V { didSet { notify() } }

    public init(_ initital: V) {
        value = initital
    }

    public func addObserver(_ object: AnyObject, skipFirst: Bool = true, closure: @escaping (V) -> Void) {
        let wrapper = ClosureWrapper(closure)
        let reference = "observer\(UUID().uuidString)".replacingOccurrences(of: "-", with: "")
        observers.setObject(wrapper, forKey: object)

        // Giving the closure back to the object that is observing
        // allows ClosureWrapper to die at the same time as observing object
        objc_setAssociatedObject(object, reference, wrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        if !skipFirst { closure(value) }
    }

    private func notify() {
        let enumerator = observers.objectEnumerator()
        while let wrapper = enumerator?.nextObject() { (wrapper as? ClosureWrapper<V>)?.closure(value) }
    }
}