class NamedShape{
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDecription() -> String {
return "A shape with \(numberOfSides) \(name) sides"
}
}
// 除了储存简单的属性之外,属性可以有 getter 和 setter
class EquilaterTriangle: NamedShape{
var sideLength: Double = 0.0
init(sideLength: Double,name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set{
sideLength = newValue / 3.0
}
}
override func simpleDecription() -> String {
return "An equilateral triagle with sides of length \(sideLength)"
}
}
var triangle = EquilaterTriangle.init(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
// 在 perimeter 的 setter 的方法中 新值的名称是 newValue , 你可以在 set 之后显式的设置一个名称
// 处理变量的可选值时候,你可以在操作 (比如方法,属性 和 子脚本) 之前加 ? ,如果 ? 之前的值是nil. ? 之后的东西都会被忽略, 并且整个表达式返回nil, 否则, 并且整个表达式返回的东西都会被运行, 这两种情况下,整个表达式的值也是一个可选值
let optionalSquare: EquilaterTriangle? = EquilaterTriangle.init(sideLength: 3.2, name: "houdehcneg")
let sidelength = optionalSquare?.sideLength
print(sidelength!)
// 枚举和结构体
// 使用 enum 来创建一个枚举,就像类和其他所有命名类型一样,枚举可以包含方法
enum Rank: Int{
case Ace = 1
case Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten
case Jack,Queen,King
func simleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue
print(ace,aceRawValue)
// 默认情况下, Switch 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变,在上面的例子中,ace 被显式赋值为 1 ,并且剩下的原始值会按照顺序赋值,你也可以使用字符串或者浮点数作为枚举的原始值,使用 rawValue 属性来访问一个枚举成员的原始值
// 协议和扩展
// 使用 protocol 来声明一个协议
protocol ExampleProticol{
var simpleDescription: String { get }
mutating func adjust()
}
// 类,枚举和结构体都可以实现协议
class SimpleClass: ExampleProticol{
var simpleDescription: String = "A very simple class"
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted"
}
}
var a = SimpleClass.init()
a.adjust()
let aDescription = a.simpleDescription
print(aDescription)
struct SimpleStructure: ExampleProticol{
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += "(adjusted)"
}
}
var b = SimpleStructure.init()
b.adjust()
let bDescription = b.simpleDescription
print(bDescription)
// 注意声明 simpleStrcture 时候 mutating 关键字用来标记一个会修改结构体的方法, SimpleClass 的声明不需要标记任何方法, 因为类中的方法通常可以修改类属性 (类的性质)
// 使用 extension 来为现有的类型添加功能, 比如新的方法和计算属性, 你可以使用扩展在别处修改定义, 甚至是从外部库或者框架引入一个类型, 使得这个类型遵循某个协议
extension Int: ExampleProticol{
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(47.simpleDescription)
// 你可以\像使用其他命名类型一样使用协议名 -- 例如, 创建一个有不同类型但是都实现一个协议的对象集合, 当你处理类型是协议的值时, 协议外定义的方法不可用
// 即使 protocolValue 变量运行的类是 simpleClass ,编辑器会把它的类型当做 ExampleProtocol , 这表示你不能调用类在他实现的协议之外实现的方法或者属性
// 错误类型
// 使用采用 Error 协议的类型来表示错误
enum PrinterError: Error{
case OutOfPaper
case NoToner
case OnFire
}
// 使用 throw 来抛出一个错误并使用 throws 来表示一个可以抛出错误的函数,如果在函数中抛出一个错误, 这个函数会立即返回并调用函数的代码进行错误处理
func send(job: Int, toPrinter printerName: String) throws -> String{
if printerName == "New Has Toner" {
throw PrinterError.NoToner
}
return "Job sent"
}
// 有很多种方式可以进行错误处理 一种方式是使用 do-catch , 在 do 代码块中,使用 try 来标记可以抛出错误的代码 , 在 catch 代码块中 , 除非你重新命名 , 否则错误会被自动命名 error
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
// 泛型
// 在尖括号里写一个名字来创建一个泛型函数或者类型
func repeatItem<Item>(repeating item: Item, numberOfTime: Int) ->[Item] {
var result = [Item]()
for _ in 0 ..< numberOfTime{
result.append(item)
}
return result
}
repeatItem(repeating: "knock", numberOfTime: 4)
// 也可以创建泛型函数,方法 ,类 ,枚举 , 和结构体
// 在类型后面使用 where 来指定对类型的需求 , 比如 , 限定类型实现某一个协议, , 限定两个类型是相同的, 或者限定某个类必须有一个特定的父类
// Int 表示整型值,Double 和 Float 表示浮点型值, Bool 表示布尔型值, String 表示文本型值, Swift 还提供了三个基本的集合类型, Array, Set , Dictionary
// 与C语言一样, Swift 使用变量来进行存储并通过变量名来关联值,在swift中,广泛的使用者的值不可变的变量,他们就是常量,在swift中如果你要处理的值不需要改变,把使用常量可以让你的代码更加安全并且更加氢气的表达你的意图
// swift 还增加了 OC 中没有的高阶数据类型 如元组 (Tuple) ,元组可以让你创建或者传递一组数据,比如作为函数返回值是,可以用元组返回多个值
// swift 还提供了可选 (Optional) 类型,用于处理值缺失的情况. 可选值表示 "那儿有一个值,并且它等于 x ,或者 那儿没有值". 可选值有点像 OC 中使用 nil , 但是他可以用在任何类型上, 不仅仅是类. 可选类型比 OC 中的 nil 更加安全也更具表现力, 它是swift许多强大的特性的重要组成部分
// swift 是一门类型安全的语言, 这意味这 swift 可以让你清楚地知道值的类型, 如果你的代码期望得到一个 String ,类型安全会阻止你传入一个 Int , 同样的, 如果你的代码期望得到一个 String , 类型安全会阻止你传入一个可选的 String , 类型安全可以帮助你在开发阶段尽早的发现并修复错误
// 常量和变量
/*
常量和变量把一个名字 和一个指定类型的值关联起来,常量的值一旦设定就不能改变, 而变量的值可以随意更改
声明常量和变量
常量和变量在使用前必须声明, 用 let 来声明常量, 用 var 来声明变量
例如 :
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
这两行代码可以被理解为:
声明一个名字 maximumNumberOfLoginAttempts 的新常量, 并给他一个值 10, 然后, 声明一个名字是 currentLoginAttempt 的变量, 他的初始值的 0
你可以在一行中声明多个常量或者变量, 用逗号隔开
*/
// 类型标注
/*
当你声明常量或者变量的时候可以加上类型标注, 说明变量或者常量中要存储的值的类型, 如果要添加类型标注, 需要在常量或者变量的命名后面加上一个 冒号 和 空格 , 然后加上类型标注
例如:
var welcomMessage: String
这个例子给 welcomMessage 变量添加了类型标注, 表示这个变量可以存储 String 类型的值
声明中的 冒号 代表着 "是...的类型",所以这行代码可以被理解为
"声明一个类型为 String , 名字为 welcomMessage 的变量"
"类型为 String 的意思是可以存储任意 String 类型的值"
welcomMessage 变量可以被设置为任意类型的字符串
你可以在一行中定义多个同样类型的变量, 用逗号分割, 并在最后一个变量名之后添加类型标注
注意:
一般来说很少需要些类型标注, 如果你在声明常量或者变量的时候赋给一个初始值, Swift 可以推断出这个常量或者变量的类型, 在上面的例子中, 没有给welcomMessage 赋给初始值, 所以变量 welcomMessage 的类型是通过一个类型标注实现的, 而不是通过初始值推断
*/
// 常量和变量的命名
/*
你可以用任何你喜欢的字符作为常量和变量名, 包括 Unicode 字符
常量和变量名不能包含数字符号,箭头,保留的 (或者非法的) Unicode 码位,连线与制表符,也不能以数字开头,可以再命名的其他位置使用数字
一旦你将常量或者变量声明为确定的类型, 你就不能使用相同的名字再次声明, 或者改变其村塾值的类型. 同时, 你也不能将常量或者变量互转
你可以更改现有的变量值为其他同类型的值,
但常量的值一旦确定就不能更改
*/
// 输出常量或者变量
/*
可以用 print(_:separator:terminator) 函数来输出 当前的变量或者常量的值
print(_:separator:terminator) 是一个用来输出一个或者多个值到适当输出去的全局函数. 如果你用Xcode , print(_:sparator:terminator),将会输出到 console 面板上 , separator 和 terminator 参数具有默认值, 因此你调用这个函数的时候可以忽略他们. 默认情况下, 该函数通过添加换行符来结束当前行, 如果不想换行, 可以传递一个空字符串给 terminator 参数- 例如 , print(someValue, terminator:"").
swift 用 字符串插值 的方式把常量名或者变量名当做占位符添加到字符串中, swift 会当前常量或者变量的值还踢这些占位符, 将常量或者变量放入 圆括号 内, 并在开括号前面使用反斜杠来将其转义
例如: prient("there is a \(welcomeMessage)")
*/
// 注释
/*
swift 中的注释C语言的注释非常相似, 单行注释以双斜杠 (//) 作为起始标记
多行注释 /**/ 与C语言多行注释不同的是 swift 的多行注释可以嵌套在其他的多行注释之中
*/
// 分号
/*
与其他大部分编程语言不同, swift 并不强制要求你在每条语句的结尾处使用分号 (;), 当然,你也可以按照你自己的习惯添加分号,
有一种情况下必须使用分号, 就是你打算在同一行内写多条独立的语句
*/
// 整数
/*
整数就是没有小数部分的数字, 整数可以有符号 ( 正, 负, 零), 或者无符号 ( 正 , 零)
swift 提供了 8 ,16 ,32 和 64 位的有符号和无符号类型的整数, 这些整数类型和C语言的命名方式想象, 比如 8 位无符号整形整数 是 UInt8 , 32 位有符号整数类型是 Int32 ,就像 swift 的其他类型一样 , 整数类型采用大写命名法
*/
// 整数范围
/*
你可以访问不同的整数类型的 min 和 max 属性来获取对应类型的最小值和最大值
let minValue = Uint8.min // minValue 为 0 , 是 Uint8 类型
let maxValue = Uint8.max // maxValue 为 255 , 是 Uint8 类型
*/
// Int
/*
一般来说, 你不需要专门指定整数的长度, swift 提供了一个 特殊的整数类型 Int , 长度与当前平台的原生字长相同
在 32 平台上, Int 与 Int32 长度相同
在 64 位平台上 ,与 Int64 长度相同
注意:
尽量不要使用 Uint , 统一使用 Int 可以提高代码的复用性 , 避免不同类型数字之间的转换,并且匹配数字的类型判断
*/
// 浮点型
/*
浮点型是有小数点的数字, 比如 3.1415926, 浮点数比整数类型表示的范围更大, 可以存储比 Int 类型更大或者更小的数字, swift 提供了两种有符号浮点数类型
Double 表示 64 位浮点数, 当你需要存储很大的或者很高精度的浮点数是请使用此类型
Float 表示 32 位浮点数, 精度不高的话可以使用此类型
注意:
Double 的精度很高, 至少有 15 位数字 , 而 Float 只有6 位数字,
在两种类型都匹配的情况下 , 优先匹配 Double
*/
// 类型安全和类型推断
/*
swift 是一门类型安全的语言, 类型安全的语言可以让你清楚的知道代码需要处理的值的类型吗如果你的代码需要一个 String ,你绝对不可能传进去一个 Int
由于swift 是类型安全的, 所以他会在编译你的代码的时候进行类型检查, 并把比匹配的类型标记为错误.这可以让你在开发的时候尽早发现并修复错误
当你要处理不同类型的值时,类型检查可以帮助你避免错误, 然而, 这并不是说你每次声明产量和变量的时候都需要显式指定类型, 如果你没有显式指定类型, swift 会使用类型推断 来选择合适的类型, 有了类型推断 , 编译器可以再编译代码的时候自动推断出表达式的类型, 原理很简单, 只要检查你的赋值即可
因为有类型推断 和 C语言 和 OC 比起来 swift 很少需要声明类型, 常量和变量虽然需要明确类型 , 但大部分工作并不需要自己来完成
当你声明常量或者变量并赋给初始值的时候类型推断非常有用, 当你在声明常量或者变量的时候赋给他们一个初始值即可触发类型推断
当推断浮点数类型的时候 swift 总是会选择 Double 而不是 Float
如果表达式中同时出现整数和浮点数, 会被推断为 Double 类型
*/
// 数值型类型转换
/*
通常来讲, 即使代码中的整数常量和变量已知非负, 也请使用 Int 类型, 总是使用默认的整数类型可以保证你的整数常量和变量可以直接被服用并且可以匹配整数字面量的类型推断
*/
// 整数转换
/*
不同整数类型的变量和常量可以存储不同范围的数字, Int8 类型常量或者变量可以存储数组的范围 -128 ~ 127 , 而 UInt8 类型的常量或者变量能存储的数字范围是 0 ~ 255 , 如果超出了常量或者变量可存储的范围吗编译器会报错
由于每种整数类型都可以存储不同范围的值, 所以你必须根据不同情况选择性使用数值类型转换, 这种选择性使用的方式, 可以预防隐式转换的错误让你的代码中类型转换意图变得清晰
要将一种数字类型转换成另一种, 你要用当前值来初始化一个期望值类型的数字, 这个数字的类型就是你的目标类型,
SomeType(ofIninitiaValue) 是调用swift 构造器并传入一个初始值的默认方法, 在语言内部, UInt16 有一个构造器, 可以接受一个 其他类型的值, 所以这个构造器可以用现在的 UInt8 来创建一个新的 UInt16
注意 : 比并不能传入任意类型的值, 只能传入 UInt16 内部有对应构造器的值, 不过你可以扩展现有的类型来让他可以接受其他类型的值(包括自定义类型)
*/
// 整数和浮点数转换
/*
整数和浮点数的转化必须显式指定类型:
浮点数到整数反向转换同样可以, 整数类型可以用 Double 或者 Float 类型来初始化
当用这种方式来初始化的时候, 浮点数会被截断
*/
// 类型别名
/*
类型别名 (type aliases) 就是给现有的类型定义另一个名字, 你可以使用 typealias 关键字来定义类型别名
当你想要给现有的类型起一个更有意义的名字的时候, 类型别名非常有用
例如:
typealias AudioSample = UInt16
定义了一个类型别名之后, 你可以在任何使用原始名的地方使用别名:
var maxAmplitudeFound = AudioSample.min
*/
// 布尔值
/*
swift 有一个基本的布尔值类型, 叫做 Bool ,布尔值指逻辑上的值, 因为他们只能是真或者假, swift 有两个布尔值常量, true 和 false
let orangesAreOrange = true
let turnipsAreDelicius = false
这两个值的类型会被推断为 Bool , 因为他们的初始值是布尔值常量, 就像之前提到的 Int 和 Double 一样, 如果你创建变量的时候赋值 true 和 false , 那你不需要讲常量和变量声明为: Bool 类型, 初始化常量或者变量的时候如果所赋值类型已知, 你就可以出发类型推断, 这让swift 代码更加简洁而且可读性更高
*/
// 元组
/*
元组 (tuples) 把多个值组合成一个复合值, 元组内的值可以是任意类型, 并不要求是相同类型
你可以把任意类型,任意顺序的值组合成一个元组, z这个元组可以包含所有类型
*/
let http404Error = (404, "Not Found")
// 你可以讲一个元组的内容分解成单独的常量和变量,然后就可以正常使用它们了
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// 如果你只需要一部分元组的值 , 分解的时候可以把忽略的部分用下划线 (_) 标记"
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 此外, 你也可以通过下标来访问元组中的单个元素
print("The status code is \(http404Error.0)")
print("The status message is \(http404Error.1)")
// 你可以在定义元组的时候给单个元素命名,然后通过名字来获取这些元素的值
let http200Sattus = (statusCode: 200, description: "OK")
print("The status code is \(http200Sattus.statusCode)")
print("The status message is \(http200Sattus.description)")
// 作为函数返回值, 元组非常有用,
// 注意: 元组在临时组织值的时候很有用,但是并不适合复杂的数据结构, 如果你的数据结构并不是临时使用的, 请使用类或者结构体,而不是元组
// 可选类型
/*
使用可选类型 (optional) 来处理值缺失的情况, 可选值表示: 有值, 等于 X, 或者 没有值
注意:
C 和 OC 中并没有可选类型的概念, 最接近的是 OC 中的一个特性, 一个方法要不返回一个对象要不返回nil, nil表示 "缺少一个合法的对象", 然而 这只对对象起作用,基本的 C 类型或者枚举类型不起作用, 对于这些类型, OC 方法一般会返回一个特殊值 (比如 NSNotFound) 来暗示值缺失, 这种方法假设方法的调用者知道并记得对特殊值进行判断, 然而, swift 的可选类型可以让你暗示任意类型的值缺失, 并不需要一个特殊值
*/
// 例如:
// swift 的 Int 类型 有一种构造器, 作用是将一个 String 值转换成一个 Int 值, 然而, 并不是所有的字符串都可以转换成一个整数, 字符串 "123" 可以被转换成123 ,但是字符串 "Hello world" 显然不行
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
print(convertedNumber!)
let hw = "Hello world"
let hwi = Int(hw)
// 因为该构造器可能失败 , 所以他返回一个可选类型 (optional) Int , 而不是一个 Int , 一个可选的 Int 被写作 Int? 而不是 Int , 问好暗示包含的值是可选类型, 也就是说可能包含 Int 值 也很能不包含值, (不能包含其他不类型的值, 只能是 Int 或者什么都没有)
// 可以给可选变量赋值为 nil 来表示他没有值
var severResponseCode: Int? = 404
severResponseCode = nil
// 注意:
// nil 不能用于非可选的常量或者变量, 如果你的代码中有常量或者变量需要处理值缺失的情况, 请把他们声明成对应的可选类型
// 如果你声明一个可选常量或者变量但没有赋值, 他们会被自动设置为 nil
var summ :String? // summ 会被自动设置为nil
// 注意:
// swift 的nil 和 OC 中的nil 并不一样, 在 OC 中, nil 是一个指向不存在对象的指针, 在swift 中, nil 不是指针 -- 它是一个确定的值, 用来表示值缺失的, 任意类型的可选状态都可以被设置为 nil ,不只是对象类型
// if 语句以及强制解析
/*
你可以使用 if 语句和 nil 比较 来判断一个可选值是否包含值. 你可以使用 (==) or (!=) 来执行比较, 如果可选类型有值,它将不等于 nil
当你确定可选类型的确实包含值之后, 你可以在可选的名字后面 添加一个 (!) 来获取值 , 这个 (!) 的意思是 "我知道这个可选有值, 请使用它.", 这被称为可选值的 '强制解析'
注意:
使用 ! 来获取一个不存在的可选值会导致运行时报错. 使用 ! 来强制解析值之前 , 一定要确定可选值包含一个非 nil 的值
*/
// 可选绑定
/*
使用可选绑定 (optional binding) 来判断可选类型是否包含值, 如果包含就把包含值赋给一个临时常量或者变量, 可选绑定可以用在 if 和 while 语句中, 这条语句不仅可以用来判断可选类型中是否有值, 同事可以将可选类型中的值赋给一个常量或者变量.
*/
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
}else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 这段代码可以被理解为:
// 如果 Int(possibleNumber) 返回的可选 Int 包含一个值, 创建一个叫做 actualNumber 的新常量并将可选类型包含的值赋给它
// 如果转换车成功, actualNumber 常量可以在 if 语句的第一个分支中使用, 他已经被可选类型包含的值初始化过, 所以不需要再使用 ! 后缀来获取他的值. 在这个例子中, actualNumber 只是用来输出转换结果
// 你可以在可选绑定中使用常量或者变量, 如果你想在 if 语句的第一个分支中操作 actualNumber 的值, 你可以改成 ' if var actualNumber ' , 这样可选类型包含的值会被赋给一个变量而非常量
// 注意:
// 在 if 语句中使用常量或者变量来创建一个可选绑定, 仅在 if 语句的句中(body)中才能取到值, 相反 , 在 guard 语句中使用常量和变量来创建一个可选绑定, 仅在 guard 语句外且在语句后才能取到值
// 隐式解析可选类型
/*
如上所述, 可选类型暗示了常量或者变量可以 "没有值", 可选可以通过 if 语句来判断是否有值, 如果有值的话可以通过可选绑定来解析
有时候在程序架构中, 第一次被赋值之后, 可以确定一个可选类型总会有值, 在这种情况下, 每次都要判断和解析可选值是非常低效的, 因为可以确定他总会有值
这种类型的可选状态定义为隐式解析可选类型 (implicitly unwrapped optionals) . 把想要用作可选的类型的后面的 问号 (String?) 改成 (String!) 来声明一个隐式解析可选类型
当可选类型被第一次赋值之后就可以确定之后一直有值的时候, 隐式解析可选类型非常有用, 隐式解析可选类型主要被用在 swift 中类的构造过程中
一个隐式解析可选类型其实就是一个普通的可选类型, 但是可以被当做非可选类型来使用
.并不需要每次都使用解析来获取可选值,
*/
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString
// 你可以把隐式可选类型当做一个自动解析的可选类型. 你要做的只是声明的时候把 感叹号 放到类型的结尾, 而不是每次取值的可选名字的结尾
// 注意:
// 如果你在隐式解析可选类型没有值的实收尝试取值, 会触发运行时错误, 和你在没有值的时候在普通的可选类型后面加一个 惊叹号 一样
// 你仍然可以把隐式解析可选类型当做普通可选类型来判断他是否有值
// 你也可以可选绑定中使用隐式解析可选类型来检查并解析他的值
// 注意:
// 如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型. 如果你需要在变量的生命周期中判断是否是 nil 的话, 请使用普通可选类型
// 错误处理
/*
你可以使用错误处理 (error handing) 来应对程序执行中可能出现的错误条件
相对于可选中运行值的存在与缺失来表达函数的成功与失败, 错误处理可以判断失败的原因, 并传播至程序的其他部分, 当一个函数遇到错误条件, 他能报错, 调用函数的地方能抛出错误消息并合理处理
*/
func canThrowAnReeor() throws{
// 这个函数有可能抛出错误
}
// 一个函数可以通过在声明中添加 throws 关键字来抛出错误消息. 当你的函数能抛出错误消息是, 你应该在表达式中前置 try 关键字
do {
try canThrowAnReeor()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
// 一个 do 语句创建了一个新的包含作用域, 使得错误能被传播到一个或者多个 catch 从句
// 例如:
func makeASandwich() throws {
}
// 例如:
//do {
// try makeASandwich()
// eatASandwich()
//} catch SandwichError.outOfCleanDishes {
// washDishes()
//} catch SandwichError.missingIngredients(let ingredients) {
// buyGroceries(ingredients)
//}
// 在此例中, makeAsanwich() (做一个三明治) 函数会抛出一个错误消息如果消息没有干净的盘子或者某个原料缺失, 因为 makeASanwich() 抛出错误, 函数调用被包裹在 try 表达式中, 将函数包裹在一个 do 语句中, 任何被抛出的错误会被传播到 catch 从句中
// 如果没有错误被抛出, eatASanwich() 函数被执行, 如果一个匹配 SandwichError.outOfCleanDishes 的错误被抛出 washDishes() 函数会被调用, 如果一个匹配 SandwichError.missingIngredients 的错误被抛出, buyGroceries(_:) 函数会被调用, 并且 catch 所捕捉得到的关键值 [String] 作为参数
// 断言
// 可选类型可以让你判断值是否存在, 你可以在代码中优雅地处理值缺失的情况, 然而 , 在某些情况下. 如果值缺失或者值并不满足特定的条件,你的代码可能没有办法运行, 这时候 . 你可以在您的代码中触发一个 断言 (assertion) 来结束代码运行并通过调试来找到值确实的情况
// 使用断言进行调试
// 断言会在运行时判断一个逻辑条件是否为 true , 从字面意思来说, 断言 "断言" 一个条件是否为真, 你可以使用断言来保证在运行其他代码之前, 某些重要的条件已经被满足, 如果条件判断为 true , 代码运行会被继续, 如果条件判读为 false , 代码执行结束, 你的应用被终止
// 如果你的代码在调试环境下触发了一个断言. 比如你在 Xcode 构建并运行了一个应用, 你可以清楚地看到不合法的状态发生在哪里并检查被触发时你的应用的状态, 此外, 断言允许你附加一条调试信息
// 你可以使用全局 assert(_:_:flie:line:)函数来写一个断言,想这个函数传入一个结果为 true 或者 false 的表达式以及一条信息, 当表达式的结果为 false 的时候这条信息会被显示
let age = 3
assert(age >= 0, "A person's age cannot be less than zero")
// 在这个例子中, 只有 age >= 0 为 true 的时候, 即 age 的值非负的时候, 代码才会继续执行, 如果 age 的值是负数, 就想代码中那样, age >= 0 为 false ,断言被触发, 终止应用
// 如果不需要断言信息 可以省略
assert(age >= 0)
// 注意: 当代码使用优化编译的时候, 断言将会被禁用, 例如在Xcode 中, 使用默认的target Release 配置选项来 build 时候, 断言会被禁用
// 何时使用断言
// 当条件可能为假的时候使用断言, 但是最终一定要保证条件为真, 这样你的代码才能继续运行
// 断言的适用场景
/*
1. 整数类型的下标索引被传入一个自定义下标实现, 但是下标索引值可能太大或者太小
2. 需要函数传入一个值, 但是非法的值可能导致函数不能正常运行
3. 一个可选值现在是 nil, 但是后面的代码运行需要一个非 nil 的值
注意:
断言可能导致你的应用终止, 所以你应当仔细设计你的代码来让非法条件不会出现, 然而, 当你的应用发布之后, 有时候非法条件出现, 这时使用断言可以快速发现问题
*/