2017-09-15 51 views
7

我已经以下代码:强制投,即使协议需要给定类型

import UIKit 

protocol Fooable: class where Self: UIViewController { 
    func foo() 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 
} 

let vc1: Fooable = SampleViewController() 
let vc2: Fooable = SampleViewController() 


// vc1.show(vc2, sender: nil) - error: Value of type 'Fooable' has no member 'show' 

// (vc1 as! UIViewController).show(vc2, sender: nil) - error: Cannot convert value of type 'Fooable' to expected argument type 'UIViewController' 

(vc1 as! UIViewController).show((vc2 as! UIViewController), sender: nil) 

注释行不编译。

为什么我会被迫投协议类型对象UIViewController即使Fooable协议要求,这符合它的类型从UIViewController继承?

回答

3

采用协议Fooable告诉编译器这个特定的UIViewController响应foo(),不多不多。

反之结论Fooable确实不是成为UIViewController必然。

约束Self: UIViewController只不过是另一种信息,编译器在编译时抱怨如果受影响的类不是UIViewController

你的情况注释SampleViewControllerFooable编译器知道只有SampleViewController响应foo()时。它不知道该类型实际上是UIViewController的一个子类。

所以,如果你想访问具体类的属性,不要注释具体类到协议。

但是你可以在show方法和其他公共属性/方法添加到协议

protocol Fooable: class where Self: UIViewController { 
    func foo() 
    func show(_ vc: Fooable, sender: Any?) 
} 

那么你可以因为编译器知道采用协议类型响应方法使用Fooable


合适的做法来注释类型的协议是例如当你要创建一个异源,但受限制的集合类型

let array : [CustomStringConvertible] = ["Foo", 1, false] 
array.forEach{ print("\($0)")} 

的代码打印使用description属性的三个项目,其所有项目都会回应。编译器将这三个项目识别为类型,其具有description属性,而不是String,IntBool

-1

在实例化视图控制器时,不需要将其转换为Fooable类型对象。以下作品:

import UIKit 

protocol Fooable: class where Self: UIViewController { 
    func foo() 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 
} 

let vc1 = SampleViewController() 
let vc2 = SampleViewController() 


vc1.show(vc2, sender: nil) 

任何类都可以实现此协议,但只有UIViewController将有可用的func foo()方法。

+0

是的,它工作,因为'vc1'和'vc2'现在是'SampleViewController'类型。我的帖子显然是简化的例子,例如。考虑这些对象是从函数'func buildVC(vcType:VCTypeEnum) - > Fooable'返回的。你可以发布更多的源代码吗? – zgorawski

+0

@zgorawski?没有更多细节,将很难帮助你。 – jbouaziz

+0

也没有,没有任何类可以实现此协议,请检查:https://ibb.co/ij1Wik – zgorawski

2

常见的模式是做这样的:

protocol Fooable { 
    func foo() 
    var viewController: UIViewController 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 

    var viewController: UIViewController { return self } 
} 

在斯威夫特4可以使UIViewController & Fooable类型的增值经销商。在Swift 3中使用上面的技巧。

0

首先,类的要求在这里是多余的,因为你的协议要求任何Fooable扩展UIViewController这是一个类。

其次,这种感觉就像某种对雨燕车队的一部分的监督,因为这个工程即使所有doStuff知道它的参数是他们实现Fooable,这表明你的代码应该只是工作

class Strawman { 
    let name: String 
    public func bar(_ x: Strawman) { 
     print("\(name) bars \(x.name) from entering.") 
    } 
    public init(name: String) { 
     self.name = name 
    } 
} 

protocol Fooable where Self: Strawman { 
    func foo() 
} 

class StrawFooable: Strawman, Fooable { 
    public func foo() { print("Foo!") } 
} 

let sm1 = StrawFooable(name: "Strawman1") 
let sm2 = StrawFooable(name: "Strawman2") 

// This will not compile if you define doStuff as 
// func doStuff(with x: Fooable, and y: Fooable) { 
func doStuff<T: Fooable>(with x: T, and y: T) { 
    x.bar(y) 
    x.foo() 
    y.bar(x) 
    y.foo() 
} 

// This will not compile if you annotate sm1 and sm2 as Fooable. 
doStuff(with: sm1, and: sm2) 

我的推荐? File a bug report.

PS。作为一个额外的WTF,如果你添加一个扩展符合基类,编译器崩溃!我的意思是,这样做没什么意义,但它确实不应该让编译器崩溃。