Swift代表一个泛型类

Gar*_*ies 15 generics swift

我有一个类需要在其中一个属性更改时调用委托.以下是委托的简化类和协议:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好.但是,我想让这个类变得通用.所以,我试过这个:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我以为使用a 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
    }
}
Run Code Online (Sandbox Code Playgroud)

我似乎在正确的道路上,但我仍然有两个编译器错误.上面的第二个错误仍然存​​在,以及声明delegate属性的新行MyClass:Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.

有没有办法实现这个目标?

Aar*_*sen 39

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

protocol MyClassDelegate: class {
    func valueChanged<T>(genericClass: MyClass<T>)
}
Run Code Online (Sandbox Code Playgroud)

这消除了typealias协议中a的需要,并且应该解决您已经获得的错误消息.

我不确定这是否是最适合您的解决方案的部分原因是因为我不知道valueChanged函数的调用方式和位置,因此我不知道添加泛型参数是否切实可行那个功能.如果此解决方案不起作用,请发表评论.


zne*_*eak 5

协议可以有类型要求,但不能是通用的;有类型要求的协议可以用作通用约束,但不能用于键入值。因此,如果您选择此路径,您将无法从泛型类中引用协议类型。

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

struct PseudoProtocol<T> {
    var valueWillChange: (MyClass<T>) -> Bool
    var valueDidChange: (MyClass<T>) -> Void
}
Run Code Online (Sandbox Code Playgroud)

不过,在内存管理时要格外小心,因为块对它们所引用的对象具有强引用。相反,委托通常是弱引用以避免循环。