17. 错误处理(Error Handling)
Swift 中的错误处理是响应错误以及从错误中恢复的过程。它涉及到四个步骤:定义错误类型、抛出错误、捕获错误以及处理错误。
定义错误类型
在 Swift 中,错误通常是遵循 Error 协议的类型的值。可以使用枚举来方便地定义一组相关的错误状态。
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
抛出错误
使用 throw 语句来抛出一个错误。通常在函数中,如果发生错误条件,使用 throw 来抛出错误。
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner // 如果打印机没有墨粉,则抛出错误
}
return "任务已发送" // 如果没有错误,返回“任务已发送”
}
捕获和处理错误
使用 do-catch 语句来捕获和处理错误。在 do 块中,将错误抛出的代码放在一个 try 表达式中。使用 catch 块来处理错误。
do {
let printerResponse = try send(job: 1040, toPrinter: "BiSheng")
print(printerResponse)
} catch PrinterError.onFire {
print("我会把火扑灭,然后再打印。")
} catch let printerError as PrinterError {
print("打印机错误: \(printerError).")
} catch {
print(error)
}
指定清理操作
使用 defer 语句来执行一系列代码,这些代码无论是否发生错误都将在当前代码块结束前执行。这常用于执行清理工作或释放资源。
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen) // 输出 false
18. 类型转换(Type Casting)
类型转换在 Swift 中用于检查实例的类型或将实例视为其子类或父类的实例。
类型检查(Type Checking)
- 使用
is关键字来检查一个实例是否属于某个特定子类型。
class MediaItem {
var name: String
init(name: String) { self.name = name }
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
let someItem = MediaItem(name: "Generic item")
if someItem is Movie {
print("该实例是一个电影")
} else {
print("该实例不是一个电影")
}
向下类型转换(Downcasting)
- 使用
as?或as!关键字将某类型的实例向下转换到其子类类型。
for item in library {
if let movie = item as? Movie {
print("电影: '\(movie.name)', 导演: \(movie.director)")
} else if let song = item as? Song {
print("歌曲: '\(song.name)', 艺术家: \(song.artist)")
}
}
Any 和 AnyObject 的类型转换
Any可以表示任何类型的实例,包括函数类型。AnyObject可以表示任何类类型的实例。
swift
var things: [Any] = []
things.append(0)
things.append(42.0)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
for thing in things {
switch thing {
case let someInt as Int:
print("整数: \(someInt)")
case let someDouble as Double where someDouble > 0:
print("大于零的浮点数: \(someDouble)")
case let someString as String:
print("字符串: \(someString)")
case let (x, y) as (Double, Double):
print("一个(x, y)坐标点: \(x), \(y)")
case let movie as Movie:
print("一部电影: '\(movie.name)', 导演: \(movie.director)")
default:
print("其他")
}
}
19. 嵌套类型(Nested Types)
Swift 允许你在支持的类型中嵌套定义枚举、类和结构体。这对于将相关的功能组织到嵌套的上下文中非常有用。
定义嵌套类型
可以在类、结构体或枚举内部定义另一个枚举、类或结构体。
struct BlackjackCard {
// 嵌套的 Suit 枚举
enum Suit: Character {
case spades = "♠", hearts = "♥", diamonds = "♦", clubs = "♣"
}
// 嵌套的 Rank 枚举
enum Rank: Int {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}
// BlackjackCard 的属性和方法
let rank: Rank, suit: Suit
var description: String {
var output = "花色为 \(suit.rawValue),"
output += " 值为 \(rank.values.first)"
if let second = rank.values.second {
output += " 或 \(second)"
}
return output
}
}
let theAceOfDiamonds = BlackjackCard(rank: .ace, suit: .diamonds)
print("这张牌的描述: \(theAceOfDiamonds.description)")
20. 泛型(Generics)
泛型代码允许你编写灵活、可重用的函数和类型,它们可以与任何类型一起工作。
泛型函数(Generic Functions)
泛型函数可以使用任何类型。类型参数写在一对尖括号(<T>)中。
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
泛型类型(Generic Types)
自定义的类、结构体和枚举可以是泛型的。
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
items.removeLast()
}
}
泛型扩展(Generic Extensions)
对泛型类型进行扩展,并在扩展中包含泛型方法。
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
类型约束(Type Constraints)
指定泛型类型必须继承自特定类,或符合特定的协议或协议组合。
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
关联类型(Associated Types)
为协议中的某个类型提供一个占位名,并将其作为协议定义的一部分。
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
泛型 Where 子句(Generic Where Clauses)
定义条件以实现更复杂的类型约束。
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {
// 检查两个容器的元素是否匹配
}
21. 高级运算符(Advanced Operators)
Swift 提供了一系列高级运算符,包括位运算符、溢出运算符、运算符重载以及自定义运算符。
位运算符(Bitwise Operators)
- 用于对数据的位进行操作。
- 包括:位与(
&)、位或(|)、位异或(^)和位取反(~)。
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
溢出运算符(Overflow Operators)
- 当操作数超出其类型所能存储的范围时使用。
- 包括:溢出加(
&+)、溢出减(&-)和溢出乘(&*)。
代码示例:
var potentialOverflow = Int16.max
potentialOverflow &+= 1 // 结果是 Int16.min
运算符重载(Operator Overloading)
- 允许自定义运算符的实现。
- 可以重载现有的运算符或定义新的运算符。
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
自定义运算符(Custom Operators)
- Swift 允许定义新的运算符。
- 新运算符要先声明,然后实现其功能。
prefix operator +++
prefix func +++ (vector: inout Vector2D) {
vector += vector
}
22. 访问控制(Access Control)
访问控制决定了代码中的实体(如类、结构体、方法等)的可见性和可访问性。Swift 提供了多种不同的访问级别来限制实体的访问范围。
访问级别(Access Levels)
- Public: 最高级别,用于框架的公共接口。
- Internal: 默认级别,用于应用或框架的内部结构。
- File-private: 限制在当前文件内访问。
- Private: 最严格的级别,限制在定义的作用域内访问。
public class SomePublicClass {
public var somePublicProperty = 0 // 显式公开属性
var someInternalProperty = 0 // 默认内部属性
fileprivate func someFilePrivateMethod() {} // 文件私有方法
private func somePrivateMethod() {} // 私有方法
}
fileprivate class SomeFilePrivateClass { // 文件私有类
var someFilePrivateProperty = 0
}
class SomeInternalClass { // 默认内部类
private var somePrivateProperty = 0 // 私有属性
}
访问控制语法(Access Control Syntax)
public class SomePublicClass {}
private var somePrivateVariable = 0
默认访问级别(Default Access Level)
- 默认为
Internal,适用于大多数情况。
class SomeDefaultInternalClass { // 默认为 Internal
var someInternalProperty = 0
}
单目标应用程序的访问级别(Access Levels for Single-Target Apps)
- 在单目标应用中,通常使用
Internal。
class SomeSingleTargetClass { // 默认为 Internal
var someProperty = 0
}
框架的访问级别(Access Levels for Frameworks)
- 框架顶层接口通常为
Public。 - 内部逻辑通常为
Internal。
public class SomeFrameworkClass { // 框架的公共接口
public var somePublicProperty = 0
internal var someInternalProperty = 0
}
单元测试目标的访问级别(Access Levels for Unit Test Targets)
- 使用
@testable导入语句来测试Internal访问级别的代码。
@testable import YourFrameworkName
class YourFrameworkTests: XCTestCase {
func testExample() {
let object = SomeFrameworkClass()
XCTAssertEqual(object.someInternalProperty, expectedValue)
}
}