3 回答

TA貢獻1821條經驗 獲得超5個贊
Swift 3更新
從Swift 3(特別是Xcode 8 beta 6附帶的內部版本)開始,集合類型現在可以在幕后執行,從值類型元素集合到抽象類型元素集合的轉換。
這意味著現在將編譯以下內容:
protocol SomeProtocol {}
struct Foo : SomeProtocol {}
let arrayOfFoo : [Foo] = []
let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo
let arrayOfAny : [Any] = arrayOfFoo
迅捷前3
這一切都始于Swift中的泛型是不變的,而不是協變的。記住,這[Type]只是的語法糖Array<Type>,您可以抽象出數組并Any希望更好地看到問題。
protocol Foo {}
struct Bar : Foo {}
struct Container<T> {}
var f = Container<Foo>()
var b = Container<Bar>()
f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'
與類類似:
class Foo {}
class Bar : Foo {}
class Container<T> {}
var f = Container<Foo>()
var b = Container<Bar>()
f = b // error: cannot assign value of type 'Container<Bar>' to type 'Container<Foo>'
Swift中的泛型根本不可能實現這種協變行為(向上轉換)。在您的示例中,由于不變性,Array<SomeStruct>被視為完全不相關的類型Array<Any>。
但是,數組是該規則的例外–它們可以在后臺默默地處理從子類類型到超類類型的轉換。但是,在將具有值類型元素的數組轉換為具有抽象類型元素(例如[Any])的數組時,它們的作用不同。
為了解決這個問題,您必須執行自己的逐個元素轉換(因為各個元素是協變的)。實現此目的的常用方法是使用map(_:):
var fooArray : [Any] = []
let barArray : [SomeStruct] = []
// the 'as Any' isn't technically necessary as Swift can infer it,
// but it shows what's happening here
fooArray = barArray.map {$0 as Any}
在這里避免隱式“幕后”轉換的一個很好的理由是由于Swift將抽象類型存儲在內存中的方式。使用“現有容器”是為了將任意大小的值存儲在固定的內存塊中–這意味著可能會為無法容納在該容器中的值進行昂貴的堆分配(僅允許引用要存儲在其中的內存)該容器)。
因此,由于數組現在在內存中的存儲方式發生了重大變化,因此禁止隱式轉換是很合理的。這使程序員可以清楚地知道,他們必須轉換數組的每個元素-導致內存結構發生這種更改(可能非常昂貴)。
有關Swift如何與抽象類型一起工作的更多技術細節,請參閱關于主題的WWDC精彩演講。有關Swift中類型差異的更多信息,請參見有關該主題的精彩博客文章。
最后,請確保在下面看到@dfri關于數組可以隱式轉換元素類型的其他情況的注釋 –即,當元素可橋接到Objective-C時,它們可以由數組隱式完成。

TA貢獻1942條經驗 獲得超3個贊
Swift無法自動在包含值類型和引用類型的數組之間進行轉換。只需將數組映射到所需的類型即可:
fooArray = barArray.map({$ 0})//是否編譯

TA貢獻1886條經驗 獲得超2個贊
由于您提到了從子類類型(和實例)的數組到超類類型的數組的轉換的例外,因此我可能要添加一個額外的例外:符合內部協議的元素數組_ObjectiveCBridgeable
(例如Int
,隱式地橋接到and NSNumber
type,UInt
,和Double
,String
以及其他)可直接分配給元素類型AnyObject
(Array<AnyObject>
)的數組,方法是在后臺使用從本機Swift類型到對應的Cocoa數據類型的每個成員隱式轉換。
添加回答
舉報