swift - 为类创建通用委托(delegate)

标签 swift generics protocols

假设我有一个非常简单的类:

class Box<T> {
  var boxedObject:T

  init(object: T) {
    self.boxedObject = object
  }
}

我现在想实现的是添加委托(delegate),它可以通知我框中的值已更改:

protocol BoxDelegate<T>: class {
    func valueInBoxChanged(box: Box<T>) -> Void
}

class Box<T> {
    var boxedObject: T {
        didSet {
            self.delegate?.valueInBoxChanged(self)
        }
    }
    weak var delegate: BoxDelegate<T>?

    init(object: T) {
        self.boxedObject = object
    }
}

这段代码当然是行不通的,因为我们没有通用委托(delegate)。我可以让委托(delegate)成为一个带有闭包的结构,但这是一个有点难看的解决方案。我应该如何在 Swift 中做这些事情?

最佳答案

假设您有一个类而不是BoxDelegate,我们称它为Listener:

public typealias ValueChanged <T> = (T) -> Void
public class Listener<T> {
    var valueChanged: ValueChanged<T>

    public init(_ valueChanged: @escaping ValueChanged<T>) {
        self.valueChanged = valueChanged
    }
}

Box 现在可以这样写:

class Box<T> {
    // the listener needs to be weak in order to be deallocated when the that is
    // observing the changes is deallocated
    weak private(set) var listener: Listener<T>?
    init(_ listener: Listener<T>) {
        self.listener = listener
    }
}

现在您可以为需要观察其状态的属性创建一个包装类:

class Observed<T> {
    var listeners: [Box<T>] = []
    private let queue: DispatchQueue = .main
    var value: T {
        didSet {
            notifyListeners()
        }
    }
    func notifyListeners() {
        notifyListeners(value)
    }
    
    func notifyListeners(_ value: T) {
        queue.async { [weak self] in
            self?.listeners.forEach { (box) in
                box.listener?.valueChanged(value)
            }
        }
    }
    func bind(_ listener: Listener<T>) {
        listeners.append(Box(listener))
    }
    
    func bind(_ listener: @escaping ValueChanged<T>) -> Listener<T> {
        let listener = Listener(listener)
        listeners.append(Box(listener))
        return listener
    }
    init(_ value: T) {
        self.value = value
    }
}

甚至有一个属性包装器:

@propertyWrapper
struct Bind<T> {
    let observable: Observed<T>
    init(wrappedValue: T) {
        observable = Observed(wrappedValue)
    }
    
    var wrappedValue: T {
        get {
            observable.value
        }
        set {
            observable.value = newValue
        }
    }
    
    var projectedValue: Observed<T> {
        observable
    }
}

用法:

var text = ""
class ViewModel {
    @Bind
    var isOn: Bool = false
}

let viewModel = ViewModel()

let bond = viewModel.$isOn.bind { isOn in
    if isOn {
        text = "on"
    } else {
        text = "off"
    }
    
}
viewModel.isOn = true
RunLoop.main.run(until: Date().advanced(by: 0.1))
text
viewModel.isOn = false
RunLoop.main.run(until: Date().advanced(by: 0.1))
text

In a playground

关于swift - 为类创建通用委托(delegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36130015/

相关文章:

iOS Swift 3 - 面板点击后删除覆盖 UIView

ios - NSInvalidUnarchiveOperationException 仅在 iOS 扩展中引发,而不是在主应用程序中引发

swift - 在 Swift 中编写一个通用的主题-观察者实现

protocols - SSH 和 HTTP 有什么区别?

protocols - 存储大小受限协议(protocol)的前向兼容性

swift - GoogleDataTransport 在 iOS14 上抛出双引号包含在框架标题中预期的尖括号错误

swift - 使用菜单从不同的应用程序启动不同的 Storyboard

haskell - 使用简单求和类型的所有值构造 n 元乘积

c# - 使用 List<T> 而不是 IEnumerable<T> 有什么好处?

c++ - 我如何将整个类从一个进程发送到另一个进程以避免序列化?