在Swift中,如何声明符合一个或多个协议的特定类型的变量?

Dan*_*sko 93 objective-c ios swift swift-protocols

在Swift中,我可以通过如下声明来明确设置变量的类型:

var object: TYPE_NAME
Run Code Online (Sandbox Code Playgroud)

如果我们想更进一步并声明一个符合多个协议的变量,我们可以使用protocol声明:

var object: protocol<ProtocolOne,ProtocolTwo>//etc
Run Code Online (Sandbox Code Playgroud)

如果我想声明一个符合一个或多个协议并且也是特定基类类型的对象,该怎么办?Objective-C等价物如下所示:

NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
Run Code Online (Sandbox Code Playgroud)

在Swift中,我希望它看起来像这样:

var object: TYPE_NAME,ProtocolOne//etc
Run Code Online (Sandbox Code Playgroud)

这使我们能够灵活地处理基类型的实现以及协议中定义的添加接口.

还有另一种我可能会失踪的更明显的方式吗?

举个例子,假设我有一个UITableViewCell负责返回符合协议的单元的工厂.我们可以轻松设置一个返回符合协议的单元格的泛型函数:

class CellFactory {
    class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
        //etc
    }
}
Run Code Online (Sandbox Code Playgroud)

后来我想在利用类型和协议的同时将这些单元格出列

var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Run Code Online (Sandbox Code Playgroud)

这会返回错误,因为表视图单元格不符合协议...

我希望能够指定单元格是否UITableViewCell符合MyProtocol变量声明?

理由

如果您熟悉工厂模式,那么在能够返回实现特定接口的特定类的对象的上下文中这将是有意义的.

就像在我的例子中一样,有时我们喜欢定义在应用于特定对象时有意义的接口.我的表格视图单元格的例子就是这样一个理由.

虽然提供的类型并不完全符合上面提到的接口,但是工厂返回的对象也是如此,因此我希望能够灵活地与基类类型和声明的协议接口进行交互

Phi*_*tto 68

在Swift 4中,现在可以声明一个变量,它是一个类型的子类,同时实现一个或多个协议.

var myVariable: MyClass & MyProtocol & MySecondProtocol
Run Code Online (Sandbox Code Playgroud)

或作为方法的参数:

func shakeEm(controls: [UIControl & Shakeable]) {}
Run Code Online (Sandbox Code Playgroud)

苹果公司在2017年WWDC 会议上宣布了这一点:在Swift中是新的

其次,我想谈谈编写类和协议.所以,在这里我已经为UI元素引入了这个可动摇的协议,它可以提供一点震动效果来引起对自身的注意.我已经继续并扩展了一些UIKit类来实际提供这种摇动功能.现在我想写一些看似简单的东西.我只想编写一个函数,它接受一堆可以摇动的控件,然后摇动那些能够引起注意的控件.我可以在这个数组中写什么类型的?这实际上是令人沮丧和棘手的.所以,我可以尝试使用UI控件.但并非所有UI控件都可以在此游戏中使用.我可以试试shakable,但不是所有的shakable都是UI控件.实际上,在Swift 3中没有很好的方法来表示它.Swift 4引入了使用任意数量的协议组成一个类的概念.

  • 只需添加一个快速进化建议的链接https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md (3认同)
  • @VyachaslavGerchicov:你可以在它周围加上括号,然后是这样的问号:var myVariable:(MyClass&MyProtocol&MySecondProtocol)? (2认同)

rin*_*aro 30

你不能声明变量之类的

var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...
Run Code Online (Sandbox Code Playgroud)

也没有声明函数返回类型

func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }
Run Code Online (Sandbox Code Playgroud)

你可以像这样声明一个函数参数,但它基本上是向上的.

func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) {
    // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2`
}

class SubClass:BaseClass, MyProtocol1, MyProtocol2 {
   //...
}

let val = SubClass()
someFunc(val)
Run Code Online (Sandbox Code Playgroud)

截至目前,您所能做的就像:

class CellFactory {
    class func createCellForItem(item: SpecialItem) -> UITableViewCell {
        return ... // any UITableViewCell subclass
    }
}

let cell = CellFactory.createCellForItem(special)
if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> {
    asProtocol.protocolMethod()
    cell.cellMethod()
}
Run Code Online (Sandbox Code Playgroud)

有了这个,技术上cell是相同的asProtocol.

但是,对于编译器,cell只有接口UITableViewCell,而且asProtocol只有协议接口.所以,当你想调用UITableViewCell方法时,你必须使用cell变量.如果要调用协议方法,请使用asProtocol变量.

如果您确定该单元符合您不必使用的协议if let ... as? ... {}.喜欢:

let cell = CellFactory.createCellForItem(special)
let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>
Run Code Online (Sandbox Code Playgroud)

  • 我不这么认为.即使你可以这样做,`cell`也只有协议方法(用于编译器). (2认同)