【问题标题】:How do I specify that a non-generic Swift type should comply to a protocol?如何指定非泛型 Swift 类型应遵守协议?
【发布时间】:2014-08-18 18:29:36
【问题描述】:

我想实现一个 Swift 方法,它接受某种类类型,但只接受那些符合特定协议的类的实例。例如,在 Objective-C 中我有这个方法:

- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter;

其中GPUImageOutput 是一个特定的类,GPUImageInput 是一个协议。只有符合此协议的 GPUImageOutput 类才是此方法可接受的输入。

但是,上述自动生成的 Swift 版本是

func addFilter(newFilter: GPUImageOutput!)

这消除了GPUImageOutput 类符合GPUImageInput 协议的要求,这将允许传入不符合要求的对象(然后在运行时崩溃)。当我尝试将其定义为GPUImageOutput&lt;GPUImageInput&gt; 时,编译器会抛出

的错误

无法专门化非泛型类型“GPUImageOutput”

如何在 Swift 中的参数中进行这样的类和协议特化?

【问题讨论】:

  • generic 方式有什么问题?
  • 我相信我在这里发布的代码应该可以工作,如果它没有使编译器崩溃的话。我无法找到将类型用作变量的方法,除非它在泛型类中存储为var filter: Topenradar.appspot.com/radar?id=5258642664194048
  • @wjl 我注意到泛型始终出现同样的问题。 Xcode 会很好地编译代码,但是一旦使用该类,程序就会崩溃。苹果有没有回复你的错误报告?
  • @Zag:还没有回应(我可能会收到“嘿,这仍然坏了吗?”对于 beta 3)。你的问题有测试用例/openradar 吗?

标签: swift


【解决方案1】:

你必须使用泛型,这样就可以了:

鉴于这些协议、主类和子类的示例声明:

protocol ExampleProtocol {
    func printTest()   // classes that implements this protocol must have this method
}

// an empty test class
class ATestClass
{

}

// a child class that implements the protocol
class ATestClassChild : ATestClass, ExampleProtocol
{
    func printTest()
    {
        println("hello")
    }
}

现在,您要定义一个方法,该方法接受符合协议 ExampleProtocol 的 ATestClass(或子类)类型的输入参数。 像这样写方法声明:

func addFilter<T where T: ATestClass, T: ExampleProtocol>(newFilter: T)
{
    println(newFilter)
}

你的方法,在 swift 中重新定义,应该是

func addFilter<T where T:GPUImageOutput, T:GPUImageInput>(newFilter:T!)
{
    // ...
}

编辑:

作为您的最后一条评论,枚举上的泛型示例

    enum OptionalValue<T> {
        case None
        case Some(T)
    }
    var possibleInteger: OptionalValue<Int> = .None
    possibleInteger = .Some(100)

专精于协议一致性:

    enum OptionalValue<T where T:GPUImageOutput, T:GPUImageInput> {
        case None
        case Some(T)
    }

编辑^2:

即使是实例变量,您也可以使用泛型:

假设你有一个类和一个实例变量,你希望这个实例变量只接受ATestClass类型的值并且符合ExampleProtocol

class GiveMeAGeneric<T: ATestClass where T: ExampleProtocol>
{
    var aGenericVar : T?
}

然后这样实例化它:

    var child = ATestClassChild()
    let aGen = GiveMeAGeneric<ATestClassChild>()
    aGen.aGenericVar = child

如果child 不符合协议ExampleProtocol,则不会编译

【讨论】:

  • 是的,这似乎确实适用于函数(并且在将 Objective-C 标头引入 Swift 时可能是 Clang 的更好映射)。但是,是否可以将这些泛型用于枚举中的属性和参数?我现在尝试这样做,编译器告诉我“只有语法函数类型可以是通用的”。也许我弄错了它们的语法。
  • 如果我理解您的要求,这是可能的。我在答案的末尾附上了书中的一个例子
  • 虽然这不是我上面问的具体内容,但我想我只是想涵盖在 Objective-C 中使用 GPUImageOutput 类型的所有情况。函数是一种情况,但另一种情况是该类型的属性。你还能将这种泛型风格用于属性吗?我想我可以把它作为一个单独的问题分开。
  • 是的,您仍然可以以相同的方式将泛型与枚举/结构一起使用:enum SomeEnum&lt;T where T:aClass, T:aProtocol&gt; {}。然后在这个枚举中声明一个属性为T
  • @BradLarson,即使有属性,你也可以使用泛型,我附上另一个例子
【解决方案2】:

这个来自 ObjC 的方法头:

- (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter { ... }

Swift 中的此标头相同:

func addFilter<T: GPUImageOutput where T: GPUImageInput>(newFilter: T?) { ... }

两种方法都接受相同的类集

  • 基于GPUImageOutput类;和
  • 符合GPUImageInput协议;和
  • newFilter是可选的,可以是nil

【讨论】:

  • 你知道如何将属性存储为&lt;T: GPUImageOutput where T: GPUImageInput&gt;吗?或者就我而言,一个数组
【解决方案3】:
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多