以下代码编译时没有警告:
var anything: Any
anything = "woof"
可以理解... Any是任何类型,值类型或引用类型。
但是,如果我们创建一个Double?
之类的可选变量,则此代码会引发警告:
var anything: Any
let aDouble: Double? = 3
anything = aDouble
但是此代码不会发出警告:
enum AnEnum<T>: {
case first
case second (T)
}
var anEnum: AnEnum<String> = .first
anything = anEnum
您可以合理地认为版本2会引发警告,因为Any
不是Optional
类型,而Double?
是Optional
类型。尝试将Optional分配给非可选类型是一种类型不匹配。
但是,在幕后,Optional
是enum
,具有.none
的情况,而在.some
情况下,.some
的情况是相关值。我的版本3使用了enum
,AnEnum
,它也有2种情况,其中第二种情况具有关联的值。 AnEnum
类型几乎与Swift本机Optional
类型相同。
为什么可以为anything
分配AnEnum值,但是不能为anything
分配Optional值呢?
(我开始回答这个问题:Swift dictionary with mix types (optional and non-optional))
然后意识到我真的不知道答案。
答案 0 :(得分:6)
Optional
不仅仅是一个枚举。这也是每种类型的隐式提升。 Any
和Optional
以奇怪的方式组合在一起,这可能令人惊讶。例如,T?
是Any
。 T??
也是Any
。 Any
是Any
,Any?
是Any
。 Any??
也是Any
。
这引起了许多麻烦,并且微妙的向上和向下转换使得不清楚Any
的可选项性是什么。例如。
var anything: Any = ""
let aDouble: Double? = 3
anything = aDouble as Any
let x = anything as? Double
这里的x
是什么?好吧,aDouble
是Optional<Double>
,而不是Double
,因此as? Double
应该返回nil
。但这不是因为魔术。它返回Optional(3)
。坦率地说,这很奇怪(尝试使用MyOptional版本作为枚举来获取相同的东西),但是反之则真的很糟糕。考虑:
let data: [String: Any] = ["aDouble": aDouble]
let y = data["aDouble"] as? Double
y
在此处是Double?
,而不是Double??
,因为可选的合并。并谢天谢地。但这很奇怪。而且,如果您有涉及基于Optional
的一致性的泛型,那么这可能会非常令人困惑(并且确实如此;因此产生了很多问题。...)现在用{{1}替换上面的Double
},它真的可以让您快速进入盗梦空间。
因此,编译器警告您将Any
与Any
混合使用是一个坏主意,并且可能导致奇怪的类型结果,因此您绝对应该在执行此操作时立即将其调用。 (也许您应该停止使用Optional
,因为无论如何它几乎从来都不是您真正想要的类型。)
要使其成为我能想到的最具体的示例,请考虑:
Any
您会认为这是并行的,但事实并非如此。当您将Optionals放入混音中时,您不能仅仅提取输入的类型。
顺便说一句,有趣的附录,您不能将var anything: Any = ""
let one: Int = 1
let maybeTwo: Int? = 2
anything = one
anything as? Int // fine
anything = maybeTwo
anything as? Int? // illegal (see below)
anything as? Int // fine (but why? that's not what we put in.)
转换为Any
类型。
Optional
这真的很有趣,因为let x = anything as? Double? // Illegal
是一个anything
:
Double?
那么我如何分辨“ (lldb) p anything
(Any) $R4 = {
payload_data_0 = 0x4008000000000000
payload_data_1 = 0x0000000000000000
payload_data_2 = 0x0000000000000000
instance_type = Double?
}
是anything
但无”和“ Double?
是anything
”之间的区别?您以为可以测试一下,但我认为不能,因为所有这些都不能始终如一地工作。相反,它试图很方便。但是你不能太用力,否则所有的接缝都会露出来。
答案 1 :(得分:6)
它有自己的历史。
从此功能开始,在Swift 3时代。
SE-0116 Import Objective-C id as Swift Any type(因此,在Swift的历史中称为id-as-Any
,在我看来,这是历史上最糟糕的事情。)
(直到Any
才在Swift程序员中不那么流行。)
它包括一个了不起的新功能,即通用桥接转换,该功能可以保证在桥接到Objective-C时,Swift中的Any
值可以转换为非空id
但是此功能导致了许多灾难性的悲剧,称为_SwiftValue
。
在Swift 3中首次引入的原始转换中,无论Optional<Double>
还是非_SwiftValue
,nil
都被转换为nil
。在Objective-C世界中,它完全没有用,_SwiftValue
上的几乎所有操作都导致崩溃,而崩溃直到运行时才发现,这抛弃了强类型语言的优点。
因此,Swift团队需要立即引入一项新功能:
SE-0140 Warn when Optional converts to Any, and bridge Optional As Its Payload Or NSNull
自Swift 3.0.1引入此功能以来,nil
被桥接到NSNull
,非nil
值则基于未包装的值被桥接。
此功能还包括将Optional
转换为Any
时的警告,以避免无意中将Optional
转换为Any
。
您所描述的警告是从Swift历史记录开始的。
Optional
的行为由许多编译器魔术(由Swift团队成员所谓)支持,例如,对于其他枚举类型,您不能使用简单的if-let ,则不能将“可选链接”与自定义枚举一起使用。 Swift编译器将Optional
视为比普通枚举特殊的事物。
答案 2 :(得分:6)
很简单,这是因为Any就像是Roach Motel的Optionals。
罗奇汽车旅馆(Roach Motel)是一个蟑螂陷阱,其座右铭是:“蟑螂可以入住,但他们不可以退房。” Any和Optionals也是如此。您可以将Optional放入Any中,但是再也无法将其淘汰。
要了解我的意思,我们首先将 else 放入Any中,然后再次将其取出:
let s : String = "howdy"
let any : Any = s
let s2 = any as! String
s2 // "howdy"
现在让我们尝试使用Optional:
let s : String? = "howdy"
let any : Any = s
let s2 = any as! String? // error
糟糕!您不能将非可选的“ down”强制转换为可选,因此原来的可选会丢失。
由Optional包裹的东西没有丢失。您仍然可以打开它:
let s : String? = "howdy"
let any : Any = s
let s2 = any as! String
s2 // "howdy"
但是现在s2
是一个字符串,而不是可选的。 Optional永远消失了。你不能把它弄出来。您无法确定Any 中是否包含可选内容。没了。
所以这就是为什么将Optional放入Any总是引起警告的原因。编译器说:“您可以执行此操作,但是您真的了解自己在做什么吗?”而且,如果您这样做,则有一些方法可以使警告消失(错误消息会告诉您它们是什么)。
答案 3 :(得分:0)
通过执行Optional<Any>
,您可以拥有一个Any?
。编译器不知道您是否期望anything
类型为nil
或Optional.none
,因此它会发出警告以确保您了解情况。
答案 4 :(得分:-1)
AnEnum
与Optional
有何不同。
您可以通过实现Optional
所做的一切来模仿Optional
。但是,您创建的是另一个独立的“可选”,它不能与Swift内置Optional
互操作。例如,您可以实现ExpressibleByNilLiteral
,以便可以将nil
分配给AnEnum
,而解析为.first
。就像Optional
将nil
解析为Optional.none
enum AnEnum<T> : ExpressibleByNilLiteral {
case first
case second (T)
public init(nilLiteral: ())
{
self = .first
}
}
var anEnum: AnEnum<String> = nil
var anything = anEnum
但是,此nil
与nil
的{{1}}不同。
根据实现建议,Optional
不直接使用Optional
的值,它只是将nil
的文字解析为实际存储的nil
。
Optional.none
与其他Enum
相同,只是一个未知值(至少您不能保证它等于任何其他值),因为未指定Optional.none
。
您的rawValue
将是nil
,而AnEnum.first
的{{1}}将是Optional
。 nil
和Optional.none
是两个不同的不相关枚举值。 Optional.none
不会将AnEnum.first
视为Optional
,因为它们是不同的,而获得AnEnum.first
的唯一方法是将Optional.none
的文字赋值给Optional.none
。
这说明了为什么可以毫无问题地将实际认为是Swift本机nil
的{{1}}分配给Optional
的原因,将您认为与Swift本机AnEnum
几乎相同的Optional
分配给Any
到Optional
确实会引起问题。因为您的自定义“可选”对编译器来说除了Any
以外没有其他特殊含义。即使从技术上讲,它只是Enum
,编译器也只对待Swift本机Optional
。