可选链接的动态类型与赋值不同

dynamicType of optional chaining not the same as assignment

可选链接returns总是一个可选值。

To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a nonoptional value.

The Swift Programming Language

为什么操场上的类型不是可选的?

let stringOptEmpty: String? = ""
stringOptEmpty?.isEmpty // is true
stringOptEmpty?.isEmpty.dynamicType // Bool.Type

但是下面的代码是

let isOk = stringOptEmpty?.isEmpty.dynamicType
isOk.dynamicType // Optional<Bool.Type>.Type

TLDR;

playground sidebar/column 将动态解析 playground 中的表达式,无论是分配给 variables (mutables/immutables) 的值还是只是 "free-floating"未赋值。

您的第一个示例将 dynamicType 应用于 ,它将解析为该特定值 的 类型(true.dynamicType: Bool.Type).

另一方面,您的第二个示例将 dynamicType 应用于 变量 (不可变,但我将使用 变量value) 不同,后者必须具有具体类型,因此将解析为可以容纳任何类型的包装值的类型(truefalse) 以及 nil(这里,nil 具体来说是 Optional<Bool.Type>.None),无论变量实际持有什么值。因此,在您的第二个示例中,dynamicType 将解析为 Optional<Bool.Type>.Type


详情

playground中显示的值sidebar/column一般遵循以下显示规则:

  • 对于赋值表达式,边栏中显示的值是分配的值,例如

    var a = 4 // shows '4'
    a = 2     // shows '2'
    let b: () = (a = 3)
              /* shows '()': the _value_ assigned to 'b', which is the _result_
                 of the assignment 'a = 3', to which a _side effect_ is that 'a'
                 is assigned the value '3'. */
    
  • 对于不包含赋值的表达式,边栏中显示的值通常是表达式的结果,例如

    true          // shows 'true'
    1 > 3         // shows 'false'
    let c = 3
    c             // shows '3'
    c.dynamicType // shows 'Int.Type'
    

在您的第一个示例(第 2-3 行)中,我们没有赋值,操场将在解析dynamicType 该值。由于我们正在处理可选值,value 要么只是包装类型的值(在本例中为 true),要么值为 具体类型 .None。即使游乐场显示例如let a: Int? = nil 的结果与边栏中的 nil 一样,显示的值实际上与说 let b: String = nil[= 的 .None (nil) 不同62=]

  • 对于let a: Int? = nila实际上是Optional<Int.Type>.None,
  • 而对于 let b: String? = nilbOptional<String.Type>.None

考虑到这一点,非nil 的解析 dynamicType 将是具体的 包裹 类型(在您的示例中,Bool.Type 自然是 true 的类型),而 nil 值的解析 dynamicType 将包括常规可选和包装的类型信息。

struct Foo {
    let bar: Bool = true
}

var foo: Foo? = Foo()

/* .Some<T> case (non-nil) */
foo?.bar             // true <-- _expression_ resolves to (results in) the _value_ 'true'
foo?.bar.dynamicType // Bool.Type <-- dynamic type of the _result of expression_
true.dynamicType     // Bool.Type <-- compare with this

/* .None case (nil) */
foo = nil
foo?.bar.dynamicType // nil <-- _expression_ resolves to the _value_ 'Optional<Foo.Type>.None'
Optional<Foo.Type>.None.dynamicType
                     // Optional<Foo.Type>.Type <-- compare with this

现在,如果您将值赋值给一个变量,自然该变量必须具有具体类型。由于我们在运行时分配的值 可以是 .None.Some<T>,变量的类型必须是可以保存这两种情况的值的类型,因此, Optional<T.Type>(不管变量是 nil 还是非 nil 值)。这是您在第二个示例中显示的情况:variabledynamicType(此处不可变,但使用 variable 不同于 value) isOk 是可以容纳 .None.Some<T> 的类型,无论变量的实际值是什么,因此 dynamicType 解析为这种类型; Optional<Bool.Type>.Type.


用括号包裹表达式可以逃避 Swift 游乐场的运行时自省?

有趣的是,如果表达式在应用 .dynamicType 之前用括号括起来,那么 playground 侧边栏会将包裹表达式的 .dynamicType 解析为 类型 的表达式,就好像它的实际值是未知的。例如,(...).dynamicType 中的 (...) 被视为具有具体类型的变量,而不是运行时解析的值。

/* .Some case (non-nil) */
foo?.bar               // true
(foo?.bar).dynamicType /* Optional<Bool>.Type <-- as if (...) 
                          is a _variable_ of unknown value         */

/* .None case (nil) */
foo = nil
(foo?.bar).dynamicType /* Optional<Bool>.Type <-- as if (...) 
                          is a _variable_ of unknown value         */

我们可以进一步注意到,操场上任何用括号包裹的单独表达式都不会解析为任何东西(在侧边栏中)。如果用括号包裹表达式,就好像我们逃避了 sidebar:s 运行时自省(这可以解释为什么包裹在括号中的表达式的 dynamicType 会像 playground 无法使用这些表达式的运行时信息一样解析)

var a = 4 // shows '4'
(a = 2)   // shows nothing; can't expand or get details in sidebar

老实说,我无法解释这是为什么,并将其归类为 Swift 游乐场的特点。