为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断
Posted
技术标签:
【中文标题】为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断【英文标题】:Why Swift doesn't type inference to Any when put multiple type item in Array为什么 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断 【发布时间】:2016-08-29 16:15:03 【问题描述】:使用Xcode 7.1开发swift 2.2时有两种情况让我很困惑,请看下面的例子,谢谢
首先,在导入 Foundation 时,我声明了一个 testArray,其中包含两个项目,一个 Integer 类型 1 和一个 String 类型“hello”,我的问题是为什么 Swift 类型推断 testArray 为 Array(NSObject) 而不是 Array(Any)
import Foundation
let testArray = [1, "hello"]
print(testArray.dynamicType) //testArray is Array<NSObject>
其次,当我删除 import Foundation 时,下面的代码无法编译,错误消息是“表达式类型不明确,没有更多内容”,我的问题是为什么 Swift 不在这个中对 Array(Any) 进行类型推断情况,谢谢帮助
let testArray2 = [2, "world"]
print(testArray2)
//can't compile, error message = "Type of expression is ambiguous without more content"
【问题讨论】:
【参考方案1】:/// The protocol to which all types implicitly conform.
public typealias Any = protocol<>
Any
只是一个所有类型都隐式遵循的协议——它本身并不是一个具体的类型。 Swift 无法推断出非具体类型的数组,这就是为什么它无法推断出Any
,但会成功推断出NSObject
(Int
可以桥接到NSNumber
,String
可以桥接到NSString
– 它们都继承自NSObject
,这是一个具体类型)。
例如,考虑一下:
protocol Foo
struct Bar:Foo
struct Baz:Foo
let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context
因为Foo
是非具体类型,Swift 无法推断出它的数组。你必须明确告诉编译器你希望它的类型是什么:
let arr:[Foo] = [Bar(), Baz()]
AnyObject
也将获得相同的行为(因为它是所有 类 隐式遵守的协议——但仍不是具体类型):
class Qux
class Fox
let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context
let a1:[AnyObject] = [Qux(), Fox()] // no error
为什么 Swift 无法推断出非具体类型的数组很可能是由于该语言中非具体类型的现有限制——目前大多数非平凡类型都需要具体类型操作。 See this great Q&A for an example.
但老实说,您真的应该更多地考虑您是否实际上需要Any
的数组。我想不出拥有Any
数组的单一实际应用,因为所有内容都隐含地符合元素,因此必须保证它们什么都不做(你不能对可能是任何东西的东西调用特定方法)。当然你可以进行类型转换,但是恢复你一开始就抛弃的类型安全有什么意义呢?
您应该始终尽可能地针对特定类型。您可以为您的值构建一个包装器——这可以是一个简单的struct
来包装几个属性,或者是一个type erasure 以便将非具体类型包装在一个伪具体类型中。至少,您应该考虑创建自己的数组元素符合的协议。
【讨论】:
最后两段在这里提出了最重要的观点。老实说,NSObject
(或 AnyObject
在这里不起作用,但我看到它一直在使用)并不比 Any
更具体。
@originaluser2 你的解释真的很有帮助,非常感谢
@c41ux 乐于助人:)【参考方案2】:
因为它不会自动识别Any
的数组
如果你定义它会起作用
let testArray2 :[Any] = [2, "world"]
Foundation
库导入NS API
,它会自动将2
转换为NSNumber
和"world"
至NSString
,并自动将其转换为NSObject
的数组
【讨论】:
1.回答:有趣,他问为什么,你只说因为是这样。 2. 答:如果你使用import Foundation
,2
就是Int
,"world"
就是String
。仅仅因为没有可以找到的原生 Swift 类型(谁知道它为什么不识别 [Any]
)它需要 [NSObject]
。
@iGodric,嘿,你错了。1。看看他的错误:Type of expression is ambiguous without more content
,它无法自动识别它,因为编译器就是这样工作的。 2. NSObject
的数组必须包含 NSObjects
。如果您将执行import Foundation
并执行下一个代码print(testArray[0].dynamicType) print(testArray[1].dynamicType)
,则输出将是__NSCFNumber
,这是NSNumber(而不是Int)和_NSContiguousString
,这是NSString
而不是String。感谢Funny
的评论。
To 1. 是的,但是说“这就是编译器的工作方式”的解释是没有解释的。至 2. 你所说的是正确的,但你在我的评论中做出假设,我没有说。是的,在导入 Foundation 并创建具有多种类型的数组时,2
是 NSNumber
,"world"
是 NSString
,但这只是因为推断的类型是 [AnyObject]
。但是导入Foundation
本身并不会自动将2
转换为NSNumber
,这就是我想说的。您的回答可能具有误导性。我的第一条评论可能听起来很刺耳。以上是关于为啥 Swift 在将多个类型项放入 Array 时不会对 Any 进行类型推断的主要内容,如果未能解决你的问题,请参考以下文章
无法将“Swift.Array<Any>”类型的值转换为“Swift.Dictionary<Swift.String, Any>”