可选链(Optional Chaining)式调用是一种可以在当前值为nil
的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么会调用成功;如果可选值是nil
,那么调用将返回nil
使用可选链式调用代替强制展开
通过调用的属性、方法或下标的可选值后面放一个问号 ‘?‘,可以定义一个可选链。这一点很像在可选值后面放一个叹号’!‘来强制展开它的值。它们主要的区别在于可选值为空时可选链式调用只会调用失败,然后强制展开会将触发运行时错误。
可选链式调用定义模型
通过使用可选链式调用可以调用多层属性、方法和下标。这样可以在复杂的模型中向下访问各种子属性,并且判断能否访问子属性的属性、方法或下标
通过可选链式调用访问属性
1 | //这里没有去创建相关的类。 |
通过可选链式调用方法
1 | if john.residence?.printNumberOfRooms() != nil { |
通过可选链式调用访问下标
通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功。
1 | if let firstRoomName = john.residence?[0].name { |
链接多层可选链式调用
可选通过连接多个可选链式调用在更深的模型层次中访问属性、方法以及下标。然而,多层可选链式调用不会增加返回可选值的可选层次。
通过可可选链式访问一个Int值,将返回Int?,无论使用了多层可选链式调用。
1 | if let johnsStreet = john. john.residence?.address?.street { |
错误处理
错误处理是响应错误以及从错误中恢复的过程。
表示并抛出错误
在Swift中,错误用符合Error
协议的类型的值来表示。这个空协议表明该类型可以用于错误处理
Swift枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。
1 | enum VendingMachineError : Error { |
抛出错误使用 throw
关键字。
1 | throw VendingMachineError.insufficientFunds(coinsNeeded: 5) |
处理错误
Swift中有4种处理错误的方式。你可以把函数抛出的错误传递给调用此函数的代码、用 do-catch 语句处理错误、 将错误作为可选类型、或者断言此错误根本就不会发生。
在调用一个能抛出错误的函数、方法或者构造器之前,加上 try
关键字,或者 try? 或 try! 这种变体。
用 throwing函数 传递错误
为了标识一个函数、方法或构造器可以抛出错误,在函数声明的参数列表之后加上 throws
关键字。一个标有 throws
关键字的函数称做 throwing函数。
1 | func canThrowErrors() throws -> String |
一个throwing函数可以在其内部抛出错误,并将错误传递到函数被调用的作用域。
注意:只有throwing 函数可以传递错误。任何在某个非throwing函数内部抛出的错误只能在函数内部处理
1 |
|
因为vend(name:)方法会传递出它抛出的任何错误。在你的代码中调用此方法要么直接处理这些错误(使用do-catch,try?或try!);要么继续将错误传递下去。
1 | func buyFavouriteSnack(person: String, vendingMachine: VendingMachine) throws { |
用Do-Catch处理错误
如果在do子语句中的代码抛出了一个错误,这个错误会与catch子语句做匹配。从而决定哪条子句能处理它
1 | do { |
将错误转换成可选值
可以使用try?通过错误转换成一个可选值来处理。
1 | func someThrowingFunction() throws -> Int { |
如果someThrowingFunction() 抛出错误,x和y的值都是nil。否则x和y的值是函数的返回值。无论someThrowingFunction()的返回值是什么类型,x,y都是这个类型的可选类型。
禁用错误传递
有时你知道某个throwing函数实际上运行时是不会抛出错误的,在这种情况下,你可以再表达式前面写try!来禁止用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果有错误抛出,你会得到一个运行时错误
1 | let phone = try! loadImage(atPath: "./Resources/John") |
指定清理操作
你可以使用 defer
语句在即将离开当前代码块是执行一系列语句。该语句能执行一些必要清理工作。
defer语句块将代码的执行延迟到当前作用域退出之前。延迟执行的语句不能包含任何控制转换语句,例如break、return语句或是抛出一个错误。
1 | func processFile(filename: String) throws { |
类型转换
类型转换可以判定实例的类型,也可以将实例看做是其父类或者子类的实例。
类型转换在Swift中使用is
和as
操作符实现。你可以用这2个操作符去检查值的类型和转换它的类型,也可以用它来检查是否实现了某个协议
为类型转换定义类的层次
1 | class MediaItem {} |
检查类型
用is
来检查实例是否属于某个特定的子类。
1 | for item in library { |
向下转型
某类型的一个常量或变量可能在幕后实际上是属于一个子类。当确定这种情况时,你可以尝试向下转到它的子类型。用 as?
或 as!
向下转型可能会失败。as?返回一个你试图向下转成的类型的可选类型。as!是试图向下转型和强制解包转换结果结为一体。如果失败会触发运行时错误
1 | for item in library { |
Any 和 AnyObject
Swift 中提供了2中特殊的类型别名:
- Any 可以表示任何类型,包括函数类型
- AnyObject 可以表示任何类类型的实例
1 | var things = [Any]() |
注意:
Any类型可以表示所有类型的值,包括可选类型。Swift会在你用Any类型来表示一个可选类型的时候,会给你一个警告。如果你确实想使用Any类型来承载一个可选值,你可以使用as 操作符显示转换为Any
嵌套类型
枚举被常用于为特定的类或结果实现某些功能。类似的,枚举可以方便的定义工具类或结构体。Swift 允许定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体
嵌套类型实践
1 | struct BlackjackCard { |