可选链接的dynamicType与分配不同 [英] dynamicType of optional chaining not the same as assignment
问题描述
可选链接总是返回 一个可选值.
要反映可以在nil值上调用可选链接的事实,即使您要查询的属性,方法或下标返回非可选值,可选链接调用的结果也始终是可选值.
为什么在操场上,这种类型不是可选的?
let stringOptEmpty: String? = ""
stringOptEmpty?.isEmpty // is true
stringOptEmpty?.isEmpty.dynamicType // Bool.Type
但是下面的代码是
let isOk = stringOptEmpty?.isEmpty.dynamicType
isOk.dynamicType // Optional<Bool.Type>.Type
TLDR;
游乐场侧边栏/列将动态解析游乐场中的表达式,无论是分配给变量(变量/不可变变量)的值,还是只是自由浮动"的未分配值.
您的第一个示例将dynamicType
应用于值,它将解析为该特定值的类型(true.dynamicType
:Bool.Type
).>
另一方面,您的第二个示例将dynamicType
应用于变量(不可变,但是在这里我将使用 variable 与值),它必须具有具体的类型,因此将解析为可以容纳任何类型的包装值(true
或false
)以及nil
(此处为nil
具体来说,是Optional<Bool.Type>.None
),无论该变量实际具有什么值.因此,在第二个示例中,dynamicType
将解析为Optional<Bool.Type>.Type
.
详细信息
在游乐场侧边栏/列中显示的值通常遵循以下显示规则:
-
对于 assignment 表达式,边栏中显示的值是已分配的值,例如
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 (/result)价值.由于我们正在处理可选内容,因此 value 要么只是包装类型的值(在这种情况下为true
),要么是特定于 type 的值. .None
.即使操场上显示例如边栏中let a: Int? = nil
的结果只是nil
,实际上显示的值与.None
(nil
)相同,例如let b: String = nil
- 对于
let a: Int? = nil
,a
的 值 实际上是Optional<Int.Type>.None
, - 对于
let b: String? = nil
,b
的 值 为Optional<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
值).这是您在第二个示例中显示的情况:变量的dynamicType
(此处是不可变的,但是使用变量来不同于 value )isOk
是可以同时容纳.None
和.Some<T>
的类型,无论变量的实际值是多少,因此dynamicType
都可以解析为该类型; Optional<Bool.Type>.Type
.
用括号括住表达式可以逃避Swift Playground的运行时自省吗?
有趣的是,如果在应用.dynamicType
之前将表达式包装在括号中,则游乐场侧边栏会将包装后的表达式的.dynamicType
解析为表达式的 type ,就好像它的实际值一样未知.例如,(...).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 */
我们还可以注意到,操场上用括号括起来的任何孤单表达式都根本无法解析为任何东西(在边栏中).如果将表达式包装在括号中,就好像我们逃避了侧边栏的运行时内省(这将解释为什么包裹在括号中的表达式的dynamicType
会像操场上无法使用这些表达式的运行时信息一样解析)
var a = 4 // shows '4'
(a = 2) // shows nothing; can't expand or get details in sidebar
Tbh,我无法解释为什么会这样,并将其归类为Swift游乐场的特殊之处.
Optional chaining returns always an optional value.
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.
Why the heck does in a playground the type not optional?
let stringOptEmpty: String? = ""
stringOptEmpty?.isEmpty // is true
stringOptEmpty?.isEmpty.dynamicType // Bool.Type
But the following code is
let isOk = stringOptEmpty?.isEmpty.dynamicType
isOk.dynamicType // Optional<Bool.Type>.Type
TLDR;
The playground sidebar/column will dynamically resolve expressions in the playground, be it values assigned to variables (mutables/immutables) or just "free-floating" non-assigned values.
Your first example applies dynamicType
to a value, which will resolve to the type of that specific value (true.dynamicType
: Bool.Type
).
Your second example, on the other hand, applies dynamicType
to a variable (an immutable, but I'll use variable here to differ from value), which must have a concrete type, and hence will resolve to a type that can hold any kind of wrapped values (true
or false
) as well as nil
(here, nil
is, specifically, Optional<Bool.Type>.None
), no matter what value the variable actually holds. Hence, the dynamicType
will resolve to Optional<Bool.Type>.Type
in your second example.
Details
The value displayed in the playground sidebar/column generally follows the following display rules:
For an assignment expression, the value shown in the sidebar is the value assigned, e.g.
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'. */
For an expression that contains no assignment, the value shown in the sidebar is generally the result of the expression, e.g.
true // shows 'true' 1 > 3 // shows 'false' let c = 3 c // shows '3' c.dynamicType // shows 'Int.Type'
In your first example (lines 2-3), we have no assignment, and the playground will dynamically resolve the value(/result) of the expression prior to resolving the dynamicType
of that value. Since we're dealing with optionals, the value is either simply the value of the wrapped type (in this case, true
), or the value is a type specific .None
. Even if the playground shows e.g. the result of let a: Int? = nil
as just nil
in the sidebar, the value shown is in fact not the same .None
(nil
) as for say let b: String = nil
- For
let a: Int? = nil
, the value ofa
is in factOptional<Int.Type>.None
, - whereas for
let b: String? = nil
, the value ofb
isOptional<String.Type>.None
With this in mind, it's natural that the resolved dynamicType
of a non-nil
value will be the concrete wrapped type (in your example, Bool.Type
is naturally the type of true
), whereas the resolved dynamicType
of a nil
value will include both the general optional and the wrapped type information.
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
Now, if you assign the values to a variable, naturally the variable must have a concrete type. Since the value we assign at runtime can be either .None
or .Some<T>
, the type of the variable must be one that can hold values of both these cases, hence, Optional<T.Type>
(disregarding of whether the variable holds nil
or a non-nil
value). This is the case which you've shown in your second example: the dynamicType
of the variable (here, immutable, but using variable to differ from value) isOk
is the type that can hold both .None
and .Some<T>
, no matter what the actual value of the variable is, and hence dynamicType
resolves to this type; Optional<Bool.Type>.Type
.
Wrapping expressions in parantheses escapes the runtime introspection of the Swift Playground?
Interestingly, if an expression is wrapped in parantheses prior to applying .dynamicType
, then the playground sidebar resolves .dynamicType
of the wrapped expression as the type of the expression, as if its actual value was unknown. E.g., (...)
in (...).dynamicType
is treated as a variable with a concrete type rather than a runtime-resolved value.
/* .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 */
We can further note that any lone expression wrapped in parantheses in the playground will not resolve to anything at all (in the sidebar). It's as if we escape the sidebar:s runtime introspection if wrapping expressions in parantheses (which would explain why the dynamicType
of expressions wrapped in parantheses will resolve as if the playground cannot make use runtime information of these expressions)
var a = 4 // shows '4'
(a = 2) // shows nothing; can't expand or get details in sidebar
Tbh, I cannot explain why this is, and will categorize it as a peculiarity of the Swift playground.
这篇关于可选链接的dynamicType与分配不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!