为了账号安全,请及时绑定邮箱和手机立即绑定

为什么[SomeStruct]无法转换为[Any]?

/ 猿问

为什么[SomeStruct]无法转换为[Any]?

考虑以下:


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,但这不是这里的问题。我正在寻找一种解释,为什么会这样。


查看完整描述

3 回答

?
收到一只叮咚

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时,它们可以由数组隐式完成。


查看完整回答
反对 回复 2019-09-27
?
手掌心

Swift无法自动在包含值类型和引用类型的数组之间进行转换。只需将数组映射到所需的类型即可:

fooArray = barArray.map({$ 0})//是否编译


查看完整回答
反对 回复 2019-09-27
?
MM们

由于您提到了从子类类型(和实例)的数组到超类类型的数组的转换的例外,因此我可能要添加一个额外的例外:符合内部协议的元素数组_ObjectiveCBridgeable(例如Int,隐式地桥接到and NSNumbertype,UInt,和DoubleString以及其他)可直接分配给元素类型AnyObjectArray<AnyObject>)的数组,方法是在后台使用从本机Swift类型到对应的Cocoa数据类型的每个成员隐式转换。

查看完整回答
反对 回复 2019-09-27
  • 3 回答
  • 0 关注
  • 111 浏览
我要回答
慕课专栏
更多

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信