文章目录
  1. 1. 元组类型问题

本周太忙了,接到一个开发任务,结果就陷进去了。记得上次认真做 js 的东西还是在几年前,本身对 js 也不太熟悉,所以开发起来也很挣扎,技术难点倒没有了,还需要花点时间去完善功能。
所以本周就只整理了一个问题,这个问题也比较有趣。就好好说说。
以后每周还是每天收集一个问题,把重点问题详细讨论下。

元组类型问题

元组(Tuple)是 Swift 引入的新的数据结构。对我来讲,用处比较大的地方是在一个函数需要返回多个值的时候,在 Objective-C 里面,需要传入一些引用来返回多余的值。而 Swift 中的元组,可以把这些返回值包裹起来返回。
新的语法,会引起大家比较多的关注和讨论。事情是这样的:

Swift and/or compiler BUG? Labels in tuples part of the type? 帖子中,作者写了一段代码,质疑 Swift 编译器的警告和输出结果不一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import UIKit
// 没有标签的元组
let theDate : (String, Int) = ("January", 30)
// 带有标签的元组
let theDateLabelled : (month:String, day:Int) = theDate
theDateLabelled.month
// theDate.month //编译错误:theDate 没有指定标签
//: The warning below says `is` test is always true, yet the test fails. **This is surely contradictory?**
if (theDate is (month:String, day:Int)) {//编译警告:'is' test is always true
print("SAME TYPE")
} else {
print("DIFFERENT TYPE")
}
// 输出 DIFFERENT TYPE
if (theDate is (String, Int)) {//编译警告:'is' test is always true
print("SAME TYPE")
} else {
print("DIFFERENT TYPE")
}
// 输出 SAME TYPE

以上代码大家应该很容易看懂,不带标签和带有标签的数组在类型判断时候是区分开来的。可以理解成带有标签的元组是没带标签的元组的子集。所以第一个 if 输出是 DIFFERENT TYPE, 而第二个 if 输出是 SAME TYPE。运行结果是正常的,但是坏就坏在编译警告上面,第一个 if 编译警告提示'is' test is always true

下面的回复也很有意思, OOPer这个回复者看起来是 Swift Dev 团队的,他建议提问者去提交一个 bug report 给苹果,下个版本修复这个编译警告问题。同时也承认编译警告是一个bug。苹果的编译警告和编译错误提示是被开发者吐槽的比较多的地方。Swift 2.0版本已经改进了很多,WWDC 2015还专门说了一下这个进步,底下观众掌声一片。。。

好吧,OOPer在跟帖里面还提到另外一个帖子的讨论,详细介绍了元组的一些容易引起歧义的地方,我们继续学习一下这个帖子:Rules for tuple types, parameter types, function call syntax (again)

还是先奉上代码再说话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 首先定义2个类似的函数
func foo(a: Int, _ b: Int) { print("foo", a, b) }
func bar(a: (Int, Int)) { print("bar", a.0, a.1) }
// 定义函数的别名
typealias Foo = (Int, Int) -> Void
typealias Bar = ((Int, Int)) -> Void
typealias Baz = (((Int, Int))) -> Void
//判断类型,Foo,Bar,Baz的类型是一样的,都是(Int, Int) -> ()
print(Foo.self == Bar.self) // true
print(Foo.self == Baz.self) // true
print(foo.dynamicType) // (Int, Int) -> ()
print(bar.dynamicType) // (Int, Int) -> ()
print(foo.dynamicType == bar.dynamicType) // true
// 自定义操作符,使用 foo 和 bar 也正常
infix operator § {}
func §<ParamListType, ResultType>(fn: ParamListType -> ResultType, args: ParamListType) -> ResultType {
return fn(args)
}
let x = (1, 2)
foo § (1, 2) // foo 1 2
bar § (1, 2) // bar 1 2
foo § x // foo 1 2
bar § x // bar 1 2
//接下来正常调用 foo 和 bar 函数:
foo(x) // 正确:foo 1 2
bar(x) // 正确:bar 1 2
foo(1, 2) // 正确:foo 1 2
//bar(1, 2) // 编译错误:Extra argument in call
//foo((1, 2)) // 编译错误: Missing argument for parameter #2 in call
bar((1, 2)) // bar 1 2
// 接下来,提问者又做了新的尝试
let fooInBarsClothing: Bar = foo
let barInFoosClothing: Foo = bar
//fooInBarsClothing(1, 2) // 编译错误:Extra argument in call
barInFoosClothing(1, 2) // 正确:bar 1 2
//
fooInBarsClothing((1, 2)) //正确: foo 1 2
//barInFoosClothing((1, 2)) // 编译错误: Missing argument for parameter #2 in call
//OOPer的回复:
typealias Hoge = (Int, b: Int)->Void
func hoge(a: Int, b: Int) { print("hoge", a, b) }
hoge(1, b: 2)
var fooFunc: Foo = hoge
fooFunc(1, 2)
var barFunc: Bar = hoge
barFunc((1, 2))
var hogeFunc: Hoge = hoge
hogeFunc(1, b: 2)

代码比较长,大家请耐心看完,总结起来,就是下面的一句话:

1
The types are currently equivalent. The way calls are handled is inconsistent;

FooBar 类型判断是一样的,但是传入参数还是需要要区分,Foo传入2个参数,而Bar需要传入元组。OOPer也说会在文档中说明调用的区别。

本文代码地址:github

文章目录
  1. 1. 元组类型问题