为什么在 [String : Any] 类型的字典中隐式解包可选项不会解包

4
如果我在类中声明了一个隐式解包的可选项,然后在类型为[String: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中使用多个类型时,这并不有用。

是的,所有键必须是相同类型的。所有值也必须是相同类型的。键的类型不一定要与值的类型相同。因此,例如,您可以有[String:Int]。 - Martin Muldoon
请在 Playground 中尝试后再发表评论。我可以在同一个字典中以 [AnyHashable: Any] 类型的异构集合的形式明确指定 StringInt 的键和值。 - Tometoyou
顺便提一下,字典打印出了 ["myString": hello]。没有出现这样的错误或警告。请参见此处 - TheTiger
@Hamish,为什么将值指定为 Any 而不是 Any? 会导致强制解包?另外,我正在使用 Swift 4.1,为什么它还没有被移除? - Tometoyou
@Hamish 好的,谢谢,我明白了!如果你把你的评论发表为答案,我会接受它。 - Tometoyou
显示剩余5条评论
4个回答

10
根据SE-0054规则,只有在需要取消包装类型的情况下才会强制取消包装IUO。在您的情况下,IUO不需要被强制解包以被强制转换为Any(因为Any可以表示任何值),所以它没有被强制解包。
这种行为在以下问题和答案中进行了更详细的讨论: 您在字典中最终得到一个ImplicitlyUnwrappedOptional值是遗留行为,在最新的Swift快照中已经被删除,将来您将得到一个Optional值(因为IUO不再是一种类型)。
然而,这里需要注意的一点(我相信会让人们犯错)是,在4.1中IUO的打印方式已经改变了。
在Swift 4.0.3中,您的示例打印如下:
var aString: String! = "hello"
var params : [String : Any] = [
  "myString" : aString
]
print(params)
// This prints ["myString": hello]

当强制将IUO转换为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协议的支持在这个提交中,以便向移除该类型迈进。因此,现在使用Swift的默认打印机制(使用反射)来打印ImplicitlyUnwrappedOptional值。因此,在字典中,您将获得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)

  // ...
}

对于带有有效载荷值的隐式解包可选类型,Swift默认反射将其打印为包含封装值的some情况。但这只是暂时的;在Swift 4.1中,IUO类型目前已被弃用,但在Swift 4.2中将被移除。编译器在许多地方内部使用了IUO类型,需要 相当大的工作量来移除。因此,在4.2中,您的字典中将有 实际的 Optional 值,它们将打印为Optional("hello")

0

将这一行修改为:

var params : [String : Any] = ["myString" : aString]

目标:

var params : [String : String] = ["myString" : aString]

我已经在我的帖子中提到了这一点,但对于接受多种类型的“字典”的用例来说并不有用。 - Tometoyou
因为 Any 可以是任何东西... Int、Double、Class,所以它不知道你想要什么。你显然希望它成为一个 String 类型,所以将其强制转换为 String。[String : String] - Martin Muldoon
是的,但Int、Double和Class都是非可选类型。我本以为这意味着IUO将被解包... - Tometoyou
好的.. 很棒的问题。从字典中检索值时,它总是可选的。你必须对其进行解包。原因是字典可能为空。编译器在尝试访问该值之前不会知道这一点。 - Martin Muldoon
这个有意义吗? - Martin Muldoon

0

当你定义一个字典类型时

let dictionary = [String:Any]()

你可以在这个字典中放任何东西

比如

 dictionary["name"] = "xyz"

并且

 dictionary["code"] = 123

let dictionary = [String:String]()

你只能放置一个字符串值。

这就是为什么在访问该值时必须解包该值,因为它可以是任何东西。


0
  • Any 可以表示任何类型的实例,包括函数类型和可选类型。
  • AnyObject 可以表示任何类类型的实例。

而你的 aString 是一个实例对象,不应该用来在属性区声明,如果你把那行代码放在任何函数/实例 function() 中,它将完美地工作,因为它将成为你的实例类型。

总之,在 Swift 中不能在属性区声明实例类型。

override func viewDidLoad() {
   let params : [String : Any] = ["myString": aString]
}

即使在属性区域,也不能像下面这样做:

var aString: String! = "String"
var abc = aString

你必须在任何Method()中执行abc = aString才能让其工作。

希望这可以帮助你。


如果字典在函数外部声明,那么它如何打印输出? - TheTiger
@TheTiger 嗯,字典由键值对组成,在Swift中,所有的操作都在任何函数下执行。如果您想要在属性区域中声明一个字典——也就是在函数外和类内部,那么您必须为键和值提供字符串,像这样:var testDic: [String:Array<String>] = ["someStringKey":["1","2"]]var testDic1:Dictionary = ["someStringKey":["1","2"]] - Abhishek Mitra
我认为字典可能在任何函数体中声明,这就是为什么 OP 能够打印 params,否则它甚至无法编译。 - TheTiger
我用自己的方式做了同样的事情,并得到了预期的结果,我不知道他是如何得到那种类型的结果的。@TheTiger 但是,在属性区域中不能像问题一样声明。 - Abhishek Mitra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接