2017-09-15 31 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,如果你添加一個擴展符合基類,編譯器崩潰!我的意思是,這樣做沒什麼意義,但它確實不應該讓編譯器崩潰。