为什么隐式解包可选在 [字符串:任意] 类型的字典中不解包



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

根据 SE-0054 规定的规则,IUO 仅在需要其解包类型的上下文中强制解包。在您的情况下,IUO 不需要强制解开包装即可强制Any(因为Any可以表示任何值),所以它不是。

这些问答中更详细地讨论了这种行为:

  • Swift 3 不正确的字符串插值与隐式解包
  • 的可选
  • 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]

给你一种错觉,IUO在被迫Any时被强行打开。然而,这就是 IUO 在 Swift 4.0.3 中的打印方式——如果它们有一个值,那么它们将打印为该值,否则它们将打印为nil

var aString: String! = nil
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": nil]

在Swift 4.1 中改变的原因是,ImplicitlyUnwrappedOptionalCustom(Debug)StringConvertible的一致性在此提交中被删除,以便在删除类型本身方面取得进展。所以现在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 类型,这需要相当多的工作才能删除。因此,在 4.2 中,字典中将有实际Optional值,这些值将像Optional("hello")一样打印。

更改此行:

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

自:

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

定义类型的字典时

let dictionary = [String:Any]()

你可以把任何东西放在这本字典里

喜欢

dictionary["name"] = "xyz"

dictionary["code"] = 123

而在

let dictionary = [String:String]()

您只能放置一个字符串值

这就是为什么在访问值时必须解开值包的原因,因为它可以是任何值。

  • 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()做才能找到工作。

希望对您有所帮助。

最新更新