- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
考虑以下几点:
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]'
我一直试图找到这背后的逻辑,但没有运气。值得一提的是,如果将结构更改为类,它会完美运行。
总是可以添加变通方法并映射 fooArray 的每个对象并将它们转换为 Any 类型,但这不是这里的问题。我正在寻找一个解释,解释为什么会这样。
有人可以解释一下吗?
最佳答案
从 Swift 3(特别是 Xcode 8 beta 6 附带的版本)开始,集合类型现在可以在后台执行从值类型元素集合到抽象类型元素集合的转换。
这意味着现在将编译以下内容:
protocol SomeProtocol {}
struct Foo : SomeProtocol {}
let arrayOfFoo : [Foo] = []
let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo
let arrayOfAny : [Any] = arrayOfFoo
这一切都始于这样一个事实,即 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>
完全无关的类型由于不变性。
但是,数组有一个异常(exception)——它们可以在幕后默默地处理从子类类型到父类(super class)类型的转换。但是,当将具有值类型元素的数组转换为具有抽象类型元素的数组(例如 [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 如何处理抽象类型的更多技术细节,请参阅 this fantastic WWDC talk on the subject .如需进一步阅读 Swift 中的类型变化,请参阅此 great blog post关于这个问题。
最后,一定要看@dfri's comments below关于数组可以隐式转换元素类型的另一种情况——即当元素可桥接到 Objective-C 时,它们可以由数组隐式完成。
关于arrays - 为什么 [SomeStruct] 不能转换为 [Any]?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38829717/
我是一名优秀的程序员,十分优秀!