2017-02-03 47 views
9

FYI:雨燕的錯誤提出這裏:https://bugs.swift.org/browse/SR-3871未能從任何Swift中投射?以協議


我在哪裏鑄造不工作的奇怪的問題,但控制檯顯示它爲正確的類型。

我有一個公共的協議

public protocol MyProtocol { } 

我實現這個模塊中,與返回實例的公共方法。

internal struct MyStruct: MyProtocol { } 

public func make() -> MyProtocol { return MyStruct() } 

然後,在我的視圖控制器,我觸發與該對象賽格瑞作爲發件人

let myStruct = make() 
self.performSegue(withIdentifier: "Bob", sender: myStruct) 

所有好爲止。

問題出在我的prepare(for:sender:)方法中。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
    if segue.identifier == "Bob" { 
     if let instance = sender as? MyProtocol { 
      print("Yay") 
     } 
    } 
} 

但是,實例到MyProtocol的轉換總是返回nil

當我在控制檯中運行po sender as! MyProtocol時,它給了我錯誤Could not cast value of type '_SwiftValue' (0x1107c4c70) to 'MyProtocol' (0x1107c51c8)。但是,po sender將輸出有效的Module.MyStruct實例。

爲什麼不能投射?

(我已經成功通過拳擊我的協議的結構來解決這個問題,但我想知道爲什麼它不工作的是,如果有更好的方法來解決它)

+0

只是在這裏出門,但在這裏更改內部聲明'內部結構MyStruct:MyProtocol {}'到'公共'改變什麼? – Dennis

+0

@ Dennis Nope :( – deanWombourne

回答

13

這是很奇怪的錯誤 - 它看起來像是在一個實例被橋接到Obj-C時發生的,它被裝箱在_SwiftValue中,並且被靜態輸入爲Any(?)。那麼這個實例就不能被轉換成它符合的給定協議。

據Joe格勒夫在bug report you filed的評論:

這是一般的實例蟲「運行時動態鑄造如果需要彌合的協議不橋樑」。由於發件人被視爲_SwiftValue對象類型,並且我們試圖找到它不符合的協議,所以我們放棄,而不嘗試使用橋接類型。

更小例子是:

protocol P {} 
struct S : P {} 

let s = S() 

let val : Any = s as AnyObject // bridge to Obj-C as a _SwiftValue. 

print(val as? P) // nil 

古怪的是,第一投射到AnyObject,然後鑄造該協議似乎工作:

print(val as AnyObject as! P) // S() 

所以看來靜態打字它作爲AnyObject使Swift也檢查橋接類型的協議一致性,允許演員成功。這樣做的原因,如在由喬格勒夫另一個評論解釋是:

運行時產生了一些bug它僅嘗試一定的轉換,以深度的水平,但在執行其他轉換(所以以後不AnyObject - > bridge - > Protocol可能工作,但Any - > AnyObject - > bridge - > Protocol不會)。它應該無論如何工作。

+2

此處提出的問題:https://bugs.swift.org/browse/SR-3871 – deanWombourne

1

的問題是sender必須通過Objective-C世界,但Objective-C並不知道這個協議/結構關係,因爲Swift協議和Swift結構都不可見。取而代之的是結構的,使用一個類:如您所願

protocol MyProtocol {} 
class MyClass: MyProtocol { } 
func make() -> MyProtocol { return MyClass() } 

現在一切正常,因爲sender可以生活和Objective-C的世界連貫地呼吸。

+2

我不是說你所隔離的行爲是正確的,結構正在裝箱,但你會認爲它與協議的關係會存活下來,你可能想提出一個錯誤'bugs.swift.org'。 – matt

0

這是我的解決方案。我不想將它變成class(re:this answer),因爲我的協議正在被多個庫實現,他們都必須記得這樣做。

我去裝箱我的協議到一個結構。

public struct BoxedMyProtocol: MyProtocol { 
    private let boxed: MyProtocol 

    // Just forward methods in MyProtocol onto the boxed value 
    public func myProtocolMethod(someInput: String) -> String { 
     return self.boxed.myProtocolMethod(someInput) 
    } 
} 

現在,我只是傳遞BoxedMyProtocol的實例。