Swift中的一个功能让我有一段时间不知道......请参阅下面的代码:
class Clazz {
var foo: String = "abc";
}
let foo: Int = 1
let bar: Int? = 2
let baz: Clazz? = Clazz()
let qux: Clazz = Clazz()
let quux: (Int, String)? = (1, "abc")
foo.0 //1
foo.0.0 //1
bar.0.0.0 //{Some 2} #optional
baz.0 //{{foo "abc"}} #optional
qux.0.0 //{foo "abc"}
quux.0 //{(.0 1, .1 "abc")} #optional
quux.1 //error: doesn't have a member named '1'
quux!.1 //"abc"
据我了解,这是因为只有一个类型为T
的元素的元组将被视为T
而不是(T)
,反之亦然,因此T.0
是真的(T).0
,它返回第一个元素。
我不明白的是,如果T
是可选的,即在T?
或(T)?
的情况下,那么总是可能的是什么?访问.0
并获得准确的信息?它是一个预期的功能与一些真正的用例,或只是一个不可避免的副产品?
问题不在于如何修复'它,但为什么就像这样。
或者,这是因为我读错了...... T?.0
实际应该是(T?).0
?
答案 0 :(得分:3)
x
是元组,则x.i
将返回第i个组件。x
不是元组,则x.0
将被视为(x).0
并返回x
(而x.i
是任何i > 0
)的错误。可选项不是元组,因此x.0
会为任何可选类型返回x
。
这适用于您的
let quux: (Int, String)? = (1, "abc")
因此
quux, quux.0, quux.0.0
都是相同的,quux.1
是错误的。 quux!
是一个元组,因此
quux!.0 == 1
quux!.1 == "abc"
Relevant part of the documentation:
元组类型
...
如果括号内只有一个元素,则类型只是该元素的类型。例如,(Int)
的类型为Int
,而不是(Int)
。
更新 Swift 2.1 / Xcode 7.1.1:显然,对待任何 变量为"单元素元组"不再起作用了:
let foo: Int = 1
let bar = foo.0 // error: value of type 'Int' has no member '0'
答案 1 :(得分:1)
你问:为什么可以在Swift中访问可选元组的.0元素?
我认为答案是:可以在Swift中访问可选元组的.0元素,因为可以在Swift中访问任何类型的.0元素。
因此,您没有看到与可选元组类型相关的特殊行为。您将看到与元组访问器相关的一般行为。
那么真正的问题是,为什么会出现这种一般行为?为什么通常可以将.0元组访问器应用于Swift中的任何类型?文档声明“(Int)的类型是Int,而不是(Int)”,但这并不能解释为什么会这样或为什么它应该以另一种方式工作,以便Int可以被视为(Int)。
我不确定为什么,但我怀疑答案与元组是结构的事实有关,并且结构通常被设计为需要尽可能少的开销用于内存分配,内存解除引用和其他运行时成本。在许多情况下,这个最小开销实际上是零开销,然后在某种意义上,struct是与其内容相同的。因此,如果您创建类型Foo
的值,如下所示:
struct Foo<T> { let foo:T }
let myValue:Foo<Int> = Foo(foo: Int(2))
然后在运行时生成的值myValue
在某种意义上表示与普通Int完全相同。周围的类型信息以及Int
中包含Foo
的事实并没有转化为包含另一个或指向另一个的一个事物的某种运行时结构。
因此,如果Foo<Int>
在这个意义上是Int
,而元组是一种结构,那么像{{1}这样的1元素元组类型以同样的方式是(Int)
。或者一般来说,Int
是(T)
。如果T
是(T)
,那么出于多种目的,T
也是T
。如果(T)
是T
,那么您应该可以像访问(T)
一样访问T
。这就是为什么你可以为(T)
这么多可能类型x.0
做{。}}的原因。
在这个解释中,我对“某种意义”有点模糊,其中结构类型或元组类型与它包含的值相同。我曾提到他们在某些情况下解析相同的运行时结构的想法。在所有情况下都一样吗?我不知道。并且这种等价也可以根据类型系统来陈述,而不是根据已编译的运行时结构来陈述。那么如何更精确地指定这种等价?我不知道。我欢迎评论! :)