为类创建通用委托 [英] Create generic delegate for class

查看:48
本文介绍了为类创建通用委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Let's say I have very simple class:

class Box<T> {
  var boxedObject:T

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

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

What I would like to achieve now is to add delegate, which can inform me that value in box has changed:

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
    }
}

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

This code is of course not working, because we don't have generic delegates. I can make delegate to be a struct with closure, but it's a bit ugly solution. How I should do such things in Swift?

推荐答案

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

Let say that instead of BoxDelegate you have a class, and let's call it 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
    }
}

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

and you can now have a wrapper class for the properties whose state you need to observe:

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
    }
}

甚至还有一个属性包装器:

and even have a property wrapper:

@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

这篇关于为类创建通用委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆