Swift 通用委托 class

Swift delegate for a generic class

我有一个 class 需要在其中一个属性发生变化时调用委托。以下是委托的简化 class 和协议:

protocol MyClassDelegate: class {
    func valueChanged(myClass: MyClass)
}

class MyClass {

    weak var delegate: MyClassDelegate?

    var currentValue: Int {
        didSet {
            if let actualDelegate = delegate {
                actualDelegate.valueChanged(self)
            }
        }
    }

    init(initialValue: Int) {
        currentValue = initialValue
    }
}

一切正常。但是,我想让这个 class 通用。所以,我尝试了这个:

protocol MyClassDelegate: class {
    func valueChanged(genericClass: MyClass)
}

class MyClass<T> {

    weak var delegate: MyClassDelegate?

    var currentValue: T {
        didSet {
            if let actualDelegate = delegate {
                actualDelegate.valueChanged(self)
            }
        }
    }

    init(initialValue: T) {
        currentValue = initialValue
    }
}

这会引发两个编译器错误。首先,协议中声明 valueChanged 的行给出:Reference to generic type 'MyClass' requires arguments in <...>。其次,在 didSet 观察者中调用 valueChanged 抛出:'MyClassDelegate' does not have a member named 'valueChanged'.

我认为使用 typealias 可以解决问题:

protocol MyClassDelegate: class {
    typealias MyClassValueType
    func valueChanged(genericClass: MyClass<MyClassValueType>)
}

class MyClass<T> {

    weak var delegate: MyClassDelegate?

    var currentValue: T {
        didSet {
            if let actualDelegate = delegate {
                actualDelegate.valueChanged(self)
            }
        }
    }

    init(initialValue: T) {
        currentValue = initialValue
    }
}

我似乎走在了正确的道路上,但我仍然有两个编译器错误。上面的第二个错误仍然存​​在,并且在声明 MyClassdelegate 属性 的行中出现了一个新错误:Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.

有什么办法可以做到吗?

如果没有更多信息,很难知道什么是解决问题的最佳方案,但一种可能的解决方案是将协议声明更改为:

protocol MyClassDelegate: class {
    func valueChanged<T>(genericClass: MyClass<T>)
}

这消除了协议中 typealias 的需要,应该可以解决您收到的错误消息。

我不确定这是否是最适合您的解决方案的部分原因是因为我不知道如何或在何处调用 valueChanged 函数,所以我不知道如果向该函数添加通用参数是可行的。如果此解决方案不起作用,post 发表评论。

协议可以有类型要求但不能是通用的;具有类型要求的协议可以用作泛型约束,但它们不能用于类型化值。因此,如果你走这条路,你将无法从你的泛型 class 中引用你的协议类型。

如果您的委托协议非常简单(比如一个或两个方法),您可以接受闭包而不是协议对象:

class MyClass<T> {
    var valueChanged: (MyClass<T>) -> Void
}

class Delegate {
    func valueChanged(obj: MyClass<Int>) {
        print("object changed")
    }
}

let d = Delegate()
let x = MyClass<Int>()
x.valueChanged = d.valueChanged

你可以将这个概念扩展到一个包含一堆闭包的结构:

class MyClass<T> {
    var delegate: PseudoProtocol<T>
}

struct PseudoProtocol<T> {
    var valueWillChange: (MyClass<T>) -> Bool
    var valueDidChange: (MyClass<T>) -> Void
}

不过,在内存管理方面要格外小心,因为块对它们引用的对象有很强的引用。相比之下,委托通常是弱引用以避免循环。

您可以使用类型擦除的模板方法...

protocol HeavyDelegate : class {
  func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}  

class Heavy<P, R> {
    typealias Param = P
    typealias Return = R
    weak var delegate : HeavyDelegate?  
    func inject(p : P) -> R? {  
        if delegate != nil {
            return delegate?.heavy(self, shouldReturn: p)
        }  
        return nil  
    }
    func callMe(r : Return) {
    }
}
class Delegate : HeavyDelegate {
    typealias H = Heavy<(Int, String), String>

    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
        let h = heavy as! H // Compile gives warning but still works!
        h.callMe("Hello")
        print("Invoked")
        return "Hello" as! R
    }  
}

let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))