为什么不是可以转换为?
考虑以下几点:
struct SomeStruct {} var foo: Any! let bar: SomeStruct = SomeStruct() foo = bar // Compiles as expected var fooArray: [Any] = [] let barArray: [SomeStruct] = [] fooArray = barArray // Does not compile; Cannot assign value of type '[SomeStruct]' to type '[Any]'
我一直在试图find这背后的逻辑,但没有运气。 值得一提的是,如果你把这个结构改成一个类,它就可以正常工作。
总是可以添加一个解决方法,映射fooArray的每个对象,并将它们转换为Anytypes,但这不是问题。 我正在寻找一个解释,为什么这是这样的行为。
有人可以解释一下吗?
这个问题导致了我这个问题。
Swift 3更新
从Swift 3开始(特别是Xcode 8 beta 6的版本),集合types现在可以在从值types元素集合到抽象types元素集合的引擎转换下执行。
这意味着现在将编译以下内容:
protocol SomeProtocol {} struct Foo : SomeProtocol {} let arrayOfFoo : [Foo] = [] let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo let arrayOfAny : [Any] = arrayOfFoo
Pre Swift 3
这一切都始于Swift中的generics是不变的 – 不是协变的。 记住[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>'
这种协变行为(upcasting)根本就不可能在Swift中generics化。 在你的例子中,由于不变性, Array<SomeStruct>
被视为与Array<Any>
完全不相关的types。
但是,数组有一个例外,它可以默默地处理从子types到超types的转换。 但是,将具有值types元素的数组转换为具有抽象types元素(比如[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将抽象types存储在内存中的方式。 为了将一个任意大小的值存储在一个固定的内存块中,使用了“存在容器”(Existential Container) – 这意味着对于不能容纳在这个容器中的值,可能会发生昂贵的堆分配(只允许对内存的引用存储这个容器代替)。
因此,由于数组现在存储在内存中的这种重大变化,禁止隐式转换是相当合理的。 这使程序员明白他们必须转换数组的每个元素 – 导致这种(可能是昂贵的)内存结构的变化。
有关Swift如何处理抽象types的更多技术细节,请参阅这个梦幻般的WWDC关于这个主题的讨论 。 有关Swift中types差异的更多信息,请参阅这篇关于这个主题的博文 。
最后,请确保看到下面的@ dfri的注释关于数组可以隐式转换元素types的另一种情况 – 即当元素可桥接到Objective-C时,它们可以由数组隐式地完成。
Swift不能自动在保存值types和引用types的数组之间进行转换。 只需将数组映射到所需的types即可:
fooArray = barArray.map({$ 0})//编译