Swift4.2(基础篇要点)

Swift4.2基础篇摘要ʕ•͡ω•ʔ

本章节只是简单的摘录文档里的重要内容(或者是没记住的- -)如果想从头开始看Swift还是直接看官方文档比较好

高级类型

除了属性的类型,Swift引入了Objective-C中没有的高级类型,比如元组。元组可以让你创建与传递值的分组。你可以使用元组将函数中的多个值作为单个复合值返回。

可选类型

Swift 还引入了可以处理缺省值的可选类型。可选类型表示「要么 有 值,并且等于 X」「要么 没有 值」 。使用可选类型与在 Objective-C 中将指针和 nil 一起使用很相似,但是,可选类型适用于任何类型,不仅仅是类。可选类型不仅比 Objective-C 中的 nil 指针更安全,更具表现力,它还是 Swift 众多强大特性中的核心。

类型安全

Swift 是一门 类型安全 的语言,这意味着它有助于明确代码中的值的类型。如果代码中需要一个 String,类型安全可以防止你错误地传递给它一个 Int。同样的,类型安全可以防止你意外地将一个可选的 String 传递给一个需要非可选 String 的代码片段。在开发过程中,类型安全可以帮你尽早捕捉并修复错误。

注释

Swift 中的多行注释可以相互嵌套使用

浮点数

Double 类型可以精确到小数点后15位,而 Float 类型只有6位。
如果两种类型都能使用的情况下,优先使用 Double 类型 。

类型推断

因为有了类型推断,Swift 和 C 以及 Objective-C 相比,只需要少量的类型声明。其实常量和变量仍然需要明确的类型,但是大部分的声明工作 Swift 会帮你做。
Swift 在推断浮点值的时候始终会选择 Double (而不是Float)。

数值型字面量

整数型字面量可以写作:

  • 十进制数,没有前缀
  • 二进制数,前缀为 0b
  • 八进制数,前缀为 0o
  • 十六进制数,前缀为 0x

类型转换

SomeType(ofInitialValue) 是 Swift 中初始化一个对象的默认方式,在这个过程中需要传入一个初始值。而在底层实现中,UInt16 有一个接收 UInt8 类型的初始化构造器,所以这个构造器是用来转换 UInt8 类型 到 UInt16 类型 的。你不能在这传入 任何 类型,因为必须是 UInt16 初始化构造器允许的类型才可以。扩展现有类型的初始化构造器,让其接收新的类型(包括你自己自定义的类型)的内容在 Extensions 中可以找到。

类型别名

类型别名 就是给现有类型定义了一个另外的名字。你可以使用 typealias 关键字来声明类型别名。

布尔值

Swift 有一个基础 布尔 类型 Bool. 布尔值也被称为 逻辑值, 因为它们只能是真或假。 Swift 提供两个布尔常量,truefalse

元组

元组 将多个值组合在一起成为一个复合值。元组里面的值可以是任何类型,不需要是相同的类型。
你可以将一个元组的内容 分解 为单独的常量或变量,然后你就可以正常使用它们了:

1
2
3
4
5
6
7
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String), 等于 (404, "Not Found")
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 打印 "The status code is 404"
print("The status message is \(statusMessage)")
// 打印 "The status message is Not Found"

如果你只需要元组里的一部分值的话,分解元组的时候使用一个下划线(_) 来忽略元组里面的值:

1
2
3
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 打印 "The status code is 404"

此外,使用从零开始的下标来访问元组里单个元素的值:

1
2
3
4
print("The status code is \(http404Error.0)")
// 打印 "The status code is 404"
print("The status message is \(http404Error.1)")
// 打印 "The status message is Not Found"

定义元组时,你可以为元组中的单个元素命名:

1
let http200Status = (statusCode: 200, description: "OK")

如果你在元组里为元素命名了,那么你就可以通过元素名称去获取元素的值:\

1
2
3
4
print("The status code is \(http200Status.statusCode)")
// 打印 "The status code is 200"
print("The status message is \(http200Status.description)")
// 打印 "The status message is OK"

元组作为函数的返回值的时候十分有用。一个尝试获取一个网页的函数可能会返回一个 (Int, String) 类型来表示结果的成功或失败。相比于返回一个类型的单个值作为结果,通过返回包含两个不同类型值的一个元组作为返回值,这个函数让自己的返回值提供了更多有用的信息。更多信息,参考 函数参数与返回值

注意
元组在临时组织的值的时候很有用。元组并不适合用于创建复杂数据结构。如果你的数据结构
比较持久而不是临时使用的话,使用类或者结构体,而不是元组。更多信息,参考 结构体和类.

可选类型

注意
CObjective-C 中没有可选类型的概念。最接近的是Objective-C 中的一个方法除了返回对象之外还会返回nil 的能力,nil表示缺省一个合法的对象。然而,这仅仅对对象起作用 — 对结构体,基本C 类型或者枚举类型并不起作用。对于这些类型,Objective-C 的方法通常会返回一个特殊的值(比如 NSNotFound)来表示值缺省的情况。这种方式假设方法的调用者知道和记得对特殊值进行处理和检查。Swift 的可选类型可以让你表明任何类型的值缺省的情况,而不需要特殊值。

非可选状态下的常量或变量不能使用 nil 。在某些特定条件下,如果你代码中的常量或变量需要指定为空值,则始终将其声明为适当类型的可选值。

如果你定义了一个可选变量但没有赋值,变量将自动设置为 nil

注意
Swift 里的 nil 不同于 Objective-C 里的 nil 。 在 Objective-C 里, nil 是一个指向空对象的指针。在 Swift 里, nil 不是指针,而是某种特定类型值的缺失。 任意 类型都可以设置为 nil, 而不仅仅是对象类型。

if 语句和强制解析

你可以在通过一个 if 语句里比较可选项和 nil 的方式来确定其是否包含确定值 。执行比较需要用到“等于”操作符 == ,或者“不等于”操作符!=

如果一个可选项包含值,那么它就被认为不等于 nil:

1
2
3
4
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 打印 "convertedNumber 包含整型值."

一旦你确认可选项包含值,你就可以通过使用在可选项名称后添加!的方式来访问它的值。感叹号的含义是:“我知道这个可选项绝对有值,请使用它。”这就是对可选项值的强制解析。

1
2
3
4
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 打印 "convertedNumber 包含整型值 123."

可选绑定

使用可选绑定 optional binding 来判断一个可选类型是否包含值,如果包含就赋给一个临时的常量或者变量使这个值可用。可选绑定可以被用到ifwhile语句中,用来检差一个值是否是可选类型, 并且将值提取为一个常量或者变量。
if 语句的可选绑定可以写成如下这样:

1
2
3
if let constantName = someOptional {
statements
}

你可以使用可选绑定而不是强制解包来重写 可选类型 章节的 possibleNumber 例子:

1
2
3
4
5
6
if let actualNumber = Int(possibleNumber) {
print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("\"\(possibleNumber)\" could not be converted to an integer")
}
// Prints ""123" has an integer value of 123"

上面的代码可以被理解为:

“如果通过 Int(possibleNumber)返回的可选 Int 类型包含一个值,那么就创建一个名为 actualNumber 的新的常量并把可选类型中的值赋给它。”

如果转换成功,常量 actualNumber 可以被用在 if 语句的第一个分支中。它已经被可选类型 包含的 值初始化,因此不再需要使用后缀 ! 来获取它的值。在这个例子中,actualNumber 被简单地用来打印转换的结果。

注意
if 语句中使用可选绑定的常量和变量仅在 if 语句内可用。相反,在 guard 语句中创建的常量和变量在 guard语句后的代码中也可以使用,如上所述 Early Exit.

隐式展开可选项

通过在声明的类型后边添加一个叹号 String! 而非问号 String? 来书写隐式展开可选项。
隐式展开可选项是后台中通用的可选项,但是同样也可以像非可选值来使用,每次访问的时候不需要展开。接下来的例子中展示了在访问被明确为 String 的可选项展开值时,可选字符串和隐式展开可选字符串的行为区别:

1
2
3
4
5
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! //要求使用感叹号

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要使用感叹号

你可以把隐式展开可选项当做在每次访问他的时候被给与了自动进行展开的权限。相比于在每次调用他的时候添加一个叹号,你可以再声明的时候呀添加一个叹号。

错误处理

相比于可选项的通过值是否缺失来判断程序的执行正确与否,错误处理机制能允许你判断错误的形成原因,如果需要的话,还能将你的代码中的错误传递到程序的其他地方。
当一个函数遇到错误情况,他或 抛出 错误。这个函数的访问者会 捕捉 到这个错误并且做出适当的反应。

1
2
3
func canThrowAnError() throws {
// 这个函数可能会出错也可能不会出错
}

通过在函数声明过程中加入 throws 关键字来表明这个函数会抛出一个错误。当你调用了一个可以抛出错误的函数时,你需要再表达式前预置 try 关键字。

Swift 会自动将错误传递到他们的生效范围之内,直到他们被 catch 分局处理。

1
2
3
4
5
6
do {
try canThrowAnError()
// 无错误抛出
} catch {
// 有错误抛出
}

do 语句创建了一个新的容器范围,可以让错误被传递到不止一个的 catch 分句处理。

下面的例子演示了如何利用错误处理机制处理不同的错误情况:

1
2
3
4
5
6
7
8
9
10
11
12
func makeASandwich() throws {
// ...
}

do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}

本例中,如果没有干净的盘子或某个原料缺失的话,makeASandwich() 函数会抛出一个错误。因为 makeASandwich() 函数抛出了错误,所以对它的调用被包裹在一个 try 表达式里。将函数调用包裹进一个 do 的语句里,任何抛出的错误都会被传播到提供的 catch 从句里。

如果没有抛出错误,eatASandwich() 方法将会被调用。如果抛出了错误,并且匹配到了 outOfCleanDishes 条件的话,washDishes() 函数就会被调用。如果匹配到了 SandwichError.missingIngredients 条件,buyGroceries(_:) 函数就会被调用,并且使用 catch 捕获的关联 String 值作为参数。

断言与先决条件

断言先决条件 是程序运行时发生的检查动作。你可以使用它们来检查代码被执行之前的一些必要条件是否被满足。如果断言或先决条件中布尔值的条件等于 true,代码将会像平常一样继续执行下去。如果条件等于 false,当前程序的状态将会是无效的,并且会导致代码执行停止,程序被终止。

断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。

调试断言

使用 Swift 标准库中的 assert(_:_:file:line:) 函数来声明一个断言语句。
可以向这个函数传入一个值为 truefalse 的表达式以及如果条件为 false 的情况下的提示性信息。如下所示:

1
2
3
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// 因为 -3 小于 0,所以这个断言失败了

上面的例子中,如果 age >= 0 语句结果为 true,代码将继续执行下去,也就是说 age 的值是非负的。如果 age 的值是负数,那么上面的代码中的 age >= 0 语句将返回 false,这将导致断言失败,程序终止。

你可以省略断言提示信息 — 比如下面的代码,仅仅是单调地重复一下条件语句。

1
assert(age >= 0)

如果代码中已经检查了条件的话,可以使用 assertionFailure(_:file:line:) 函数来表明断言已经失败。如下所示:

1
2
3
4
5
6
7
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}

强制执行先决条件

只要条件可能会为 false 的时候,就使用先决条件。但代码必须 肯定 为 true 才能继续执行下去。例如,使用先决条件去检查下标是否越界或检查函数是否传入了合法的参数值。

通过调用 precondition(_:_:file:line:) 函数来声明一个先决条件。你可以向一个先决条件传入结果为 truefalse 的表达式和当结果为 false 时的提示信息。例如:

1
2
// 判断下标...
precondition(index > 0, "Index must be greater than zero.")

你也可以使用 preconditionFailure(_:file:line:) 函数来表明执行的失败 — 例如,如果一个 switch 语句的默认条件命中了,但是所有有效的输入数据只会被其他条件处理。

注意
如果你以不检查的编译模式( Ounchecked )模式进行编译,先决条件将不会起作用。编译器会假设先决条件总是为真,并会根据你的代码做相应的优化。然而,无论优化设置如何,fatalError(_:file:line:) 函数总会停止程序的执行。

你可以在原型设计和开发的早期过程中使用 fatalError(_:file:line:) 函数来创建尚未实现的功能的存根,编写 fatalError("Unimplemented") 作为存根的实现。因为 fatal error 永远不会被优化,与断言和先决条件不同的是,你可以确保程序总是在遇到存根实现时停止。