转载

《Swift by Tutorials》学习笔记(第二章)

第一章主要学习了Swift的一些基本内容,变量、常量、数据类型以及控制流。而这一章讲的是相对“高级”一点的基础知识,当然也还是基础知识啦。

可选类型(Optionals)

根据之前的知识,当我们需要声明一个变量的时候,我们可以这么做:

var str = "Hello, playground"

这样,我们在声明的时候同时指定了初值,但是,如果在有些情况下,你声明变量的时候是不知道初值的,那么应该怎么办呢?从直观上来讲,我们会这么做:

// 声明一个字符串类型的变量,但没有赋初值 var str: String

实际上,如果我们这么做,编译器会报出 Variable 'str' used before being initialized 的错误, 在Swift当中,不允许声明一个变量而不赋值,这能防止我们在没有初始化的情况下使用

变量。

那么,如果这样呢?

var str: String = nil

结果,这次报出的错误是 Type ‘string’ does not conform to protocol ‘NilLiteralConvertible’str 是一个字符串类型的变量,而 nil 不是一个字符串,所以我们不能进行赋值。

事实上,在Swift当中,如果一个变量的值有可能为空,则我们需要指定它的类型为可选类型。

可选类型的声明

当我们需要声明一个可选类型的变量,我们可以这么做:

var str: String?

只需在变量类型的后面加一个问号就可以了。在没有赋初值的情况下, str 会自动初始化为 nil

如果我们把声明改成如下:

var str: String? = "Hello Swift by Tutorials"

可以在右边的面板中看到输出结果为 {Some "Hello Swift by Tutorials!"} ,这说明 str 是一个可选类型,而非真正的字符串。

在这种情况下,如果我们要使用 str ,则必须先对其进行解包,可以这么做:

if let unwrappedStr = str {     println("Unwrapped! /(unwrappedStr.uppercaseString)") } else {     println("Was nil") }

当str的值不为 nil 时,则会将str解包,将直接赋值到unwrappedStr,然后执行第一个语句,否则会执行 else 里面的语句。

强制解包(Forced unwrapping)

当我们确定一个可选变量的值不为 nil 时,我们可以使用 强制解包 来获取变量内的值,而不必使用 if 语句来进行判断。要使用强制解包,只需要在变量的后面加一个感叹号:

var str: String? = "Hello Swift by Tutorials!" println("Force unwrapped! /(str!.uppercaseString")

如果对一个值为 nil 的变量进行强制解包,会造成一个运行时错误。

虽然强制解包很方便,但是它破坏了可选类型的安全性,只有当我们十分确定一个变量不是为 nil 时才能对它进行强制解包。

隐式解包(Implict unwrapping)

隐式解包功能可以让我们使用可选类型的时候不需要手动进行解包。要使用一个隐式解包的变量,我们需要在声明变量的时候不是使用问号,而是使用感叹号

// 声明一个隐式解包的变量 var str: String! = "Hello Swift by Tutorials"

之后,我们可以像使用普通变量一样来使用 str

str = str.lowercaseString println(str)

虽然,我们使用隐式解包变量的语法跟普通变量是一样的,但是实际是上编译器自己帮我们进行了解包,如果直接使用了一个值为 nil 的变量,还是会造成一个运行时的错误,所以在使用隐式解包变量的时候一定要注意。

可选链(Optional chaining)

使用可选链可以方便地获取一个可选变量的值,而不需要使用 if/let 的条件判断来对其进行解包。

var maybeString: String? = "Hello Swift by Tutorials" let uppercase = maybeString?.uppercaseString

第二行的问号,是可选链的语法。在运行的时候,编译器会先检查 maybeString ,如果它包含了正确的实例,则会继续执行 uppercaseString 语句,否则,则整个表达式返回 nil 。因此, uppercase 也是一个可选类型的变量。

集合类型

任何一门语言里面都必然会包括集合类型。比如 Objective-C 中的 NSArrayNSDictionary 。Swift语言里面内置了两种数据类型,即数组和字典。

数组

数组是一组相同类型变量的集合,可以使用方括号来声明:

var array = [1, 2, 3, 4, 5]

在这里,编译器会进行类型推断,因为这个数组为 Int 类型的数组,也即,这个数组里面只能存储整型类型的数据。

可以使用下标来获取特定位置的元素:

println(array[2])

也可以方便地向数组中添加元素:

array.append(6)

或者直接添加一组范围内的元素:

array.extend(7...10)

我们也可以显式地指定数组所能存储的类型:

var array: [Int] = [1, 2, 3, 4, 5]

在Swift当中有一个 AnyObject 类型,可以用来指代任何类型,如果我们声明了一个 AnyObject 类型的数组,则这个数组就可以存储任意类型的元素。虽然使用 AnyObject 看起来挺方便的,不过它破坏了Swift的类型安全机制,所以最好少用。

字典

数组存储的是一组类型的数据,而字典则存储的是一组键值对。

var dictionary = [1: "Dog", 2: "Cat"]

这行代码声明了一个包含两个键值对的字典,键和值之间使用分号隔开,而每个键值对之间使用逗号来分隔。

与数组类似,我们也可以显示声明字典的类型:

var dictionary: [Int: String] = [1: "Dog", 2: "Cat"]

可以使用方括号配合键来获得特定的值:

println(dictionary[1])

也可以对值进行更新:

dictionary[3] = "Mouse" println(dictionary)

我们可以将某个值设为 nil 来删除它:

dictionary[3] = nil

上面的代码执行后,则字典里又只包含两个键值对。

要注意到,当我们使用键来获取某个值的时候,返回的是一个可选值,因为我们要取的值有可能不包含在字典当中。

// 输出Optional("Dog") println(dictionary[1])

引用和拷贝

在Swift当中,当我们对数组或者字典进行赋值,或者在函数进行传参的时候,编译器会当整个数组或字典进行拷贝,而并非只传递了引用,这与其它很多语言有很大的不同。

var dictionaryA = [1: 1, 2: 4, 3: 9, 4: 16] var dictionaryB = dictionaryA  dictionaryB[4] = nil println(dictionaryA) println(dictionaryB)

运行之后可以看到,我们修改了字典B的元素,而字典A不会受到影响,因此可以证明在Swift当中字典确实是按值拷贝的。

对数组进行同样的操作

var arrayA = [1, 1, 2, 3, 5, 8, 13] var arrayB = arrayA  arrayB.removeAtIndex(0) println(arrayA) println(arrayB)

同样的,对数组B进行的操作不会影响到数组A。

常量集合

如果我们想使声明的集合类型为不可变的,只需要使用 let 关键字。

let constantArray = [1, 2, 3, 4, 5]

这样定义出来的数组将是不可变的,即不能修改元素的内容,也不能对数组进行增加和删除。

小结

这章主要讲了Swift当中的可选类型,可选类型是Swift当中比较独特的类型,也是用来保证Swift类型安全的重要类型,因此用到的地方会很多,有必要好好掌握。同时还介绍了Swift当中的两种集合类型,数组和字典,使用方法与其它语言也是大同小异的,因此也比较容易上手。

正文到此结束
Loading...