Swift代表一个泛型类

时间:2015-02-19 19:18:49

标签: swift generics

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

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

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

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 <...>。其次,valueChanged观察者对didSet的调用会引发:'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
    }
}

我似乎走在正确的道路上,但我仍然有两个编译器错误。上面的第二个错误仍然存​​在,以及声明delegate的{​​{1}}属性的新行:MyClass

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

3 个答案:

答案 0 :(得分:36)

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

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

这样就无需在协议中使用typealias,并且应该解决您已经收到的错误消息。

我不确定这是否是最适合您的解决方案的部分原因是因为我不知道调用valueChanged函数的方式或位置,所以我不知道&# 39;知道向该函数添加泛型参数是否切实可行。如果此解决方案无效,请发表评论。

答案 1 :(得分:4)

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

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"))

答案 2 :(得分:2)

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

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

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
}

但是要特别注意内存管理,因为块对它们引用的对象有强烈的引用。相比之下,代表通常是避免循环的弱引用。

相关问题