为啥隐式展开的可选选项不在 [String : Any] 类型的字典中展开
Posted
技术标签:
【中文标题】为啥隐式展开的可选选项不在 [String : Any] 类型的字典中展开【英文标题】:Why does implicitly unwrapped optional not unwrap in dictionary of type [String : Any]为什么隐式展开的可选选项不在 [String : Any] 类型的字典中展开 【发布时间】:2018-04-02 10:27:13 【问题描述】:如果我在我的类中声明了一个隐式展开的可选选项,然后我在 [String : Any]
类型的 Dictionary
中引用它,它不会被展开。为什么是这样?为什么Any
,不是可选的,不强制解包?
var aString: String! = "hello"
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("hello")]
请注意,如果我将字典指定为 [String : String]
类型,它将被解包,但这对于我的 Dictionary
中需要多个类型时没有用。
【问题讨论】:
字典中不能有不同的类型。根据定义 - 所有键必须属于同一类型,所有值必须属于同一类型。 @MartinMuldoon 这不是真的,请参阅:developer.apple.com/documentation/swift/dictionary 确实如此。所有密钥必须是同一类型。所有值必须属于同一类型。键的类型不必与值的类型相同。所以你可以有 [String : Int] 例如。 请先在 Playground 中尝试一下再发表评论。我可以在同一个字典中指定String
和Int
的键和值作为[AnyHashable : Any]
类型的异构集合
顺便说一句,字典正在打印["myString": hello]
。没有这样的错误或警告。 See this
【参考方案1】:
根据SE-0054 规定的规则,IUO 仅在需要其展开类型的上下文中强制展开。在你的情况下,IUO 不需要被强制解包以被强制为Any
(因为Any
可以代表任何值),所以它不是。
在这些问答中更详细地讨论了这种行为:
Swift 3 incorrect string interpolation with implicitly unwrapped Optionals Implicitly unwrapped optional assign in Xcode 8您的字典中最终得到 ImplicitlyUnwrappedOptional
值的事实是已在最新的 Swift 快照中删除的遗留行为,将来您将最终得到 Optional
值(因为 IUO 不是更长的类型)。
这里需要注意的重要一点(我肯定会绊倒人们)是 IUO 的打印在 4.1 中发生了变化。
在 Swift 4.0.3 中,您的示例打印如下:
var aString: String! = "hello"
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": hello]
给你一个错觉,当被强制到Any
时,IUO 被强制解开。然而,这正是 Swift 4.0.3 中 IUO 的打印方式——如果它们有一个值,那么它们将打印为该值,否则它们将打印为 nil
:
var aString: String! = nil
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": nil]
在 Swift 4.1 中发生这种变化的原因是 ImplicitlyUnwrappedOptional
与 Custom(Debug)StringConvertible
的一致性被删除 in this commit 以便在删除类型本身方面取得进展。所以现在ImplicitlyUnwrappedOptional
值使用 Swift 的默认打印机制(使用反射)打印。
因此,在字典中,您会得到 IUO 的默认 debugDescription
,如下所示:
let aString: String! = "hello"
let params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("hello")]
如果你自己打印它,你会得到它的默认description
,看起来像这样:
let aString: String! = "hello"
print(aString) // some("hello")
这是因为在 Swift 4.1 中,ImplicitlyUnwrappedOptional
类型的实现方式与 Optional
相同,枚举有两种情况:
public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral
// The compiler has special knowledge of the existence of
// `ImplicitlyUnwrappedOptional<Wrapped>`, but always interacts with it using
// the library intrinsics below.
/// The absence of a value. Typically written using the nil literal, `nil`.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
// ...
对于具有有效负载值的 IUO,Swift 的默认反射将因此将其打印为包含包装值的some
。
但这只是暂时的; IUO 类型目前(在 Swift 4.1 中)已弃用,但它将在 Swift 4.2 中删除。编译器内部在很多地方都使用了 IUO 类型,其中使用了quite a bit of work to remove。因此,在 4.2 中,您的字典中有实际 Optional
值,其打印结果类似于Optional("hello")
。
【讨论】:
【参考方案2】:改变这一行:
var params : [String : Any] = ["myString" : aString]
到:
var params : [String : String] = ["myString" : aString]
【讨论】:
我已经在我的帖子中提到了这一点,但这对于接受多种类型的Dictionary
的用例没有帮助
因为 Any 可以是任何东西...... Int、Double、Class,所以它不知道你想要什么。您显然希望它是 String 类型,因此请以这种方式进行转换。 [字符串:字符串]
是的,但是 Int、Double、Class 都是未包装的类型。我原以为这意味着 IUO 将被打开……
OK.. 好问题。从字典中检索值时,它始终是可选的。你必须打开它。原因是 Dictionary 可能为空。在您尝试访问该值之前,编译器不会知道。
这有意义吗?【参考方案3】:
当你定义一个字典类型时
let dictionary = [String:Any]()
你可以在这本字典里放任何东西
喜欢
dictionary["name"] = "xyz"
和
dictionary["code"] = 123
在
let dictionary = [String:String]()
你只能放一个字符串值
这就是为什么在访问值时必须解开值,因为它可以是任何东西
【讨论】:
【参考方案4】:Any
可以表示任何类型的实例,包括函数类型和可选类型。
AnyObject
可以表示任何类类型的实例。
您的aString
是一个实例对象,不应用于在属性Area 中声明,如果您将该行放在任何函数/实例function()
中,那么它将完美运行,因为它将是您的实例类型.
一句话,Swift的属性区不能声明实例类型。
override func viewDidLoad()
let params : [String : Any] = ["myString": aString]
即使在属性区,你也不能这样做:
var aString: String! = "String"
var abc = aString
你必须abc = aString
做任何Method()
才能工作。
希望对你有所帮助。
【讨论】:
如果字典在函数外声明,那么它如何打印? @TheTiger 好吧,字典由 Key Value Compliant 组成,在 swift 中,每件事都在任何函数下执行,如果你想在属性区域声明字典意味着在函数之外和类内,那么你必须为键和值提供字符串像这样:var testDic : [String : Array<String>] = ["someStringKey" : ["1", "2"]]
和 var testDic1: Dictionary = ["someStringKey" : ["1", "2"]]
我是说字典可能在任何函数体中声明,这就是为什么 OP 能够打印 params
否则它甚至无法编译。
我按照我的方式做了同样的事情,找到了预期的结果,我不知道他是怎么得到那种结果的。 @TheTiger 在哪里,您不能在属性区域中声明像问题一样。以上是关于为啥隐式展开的可选选项不在 [String : Any] 类型的字典中展开的主要内容,如果未能解决你的问题,请参考以下文章