1. 可选类型与错误处理
1.1 可选类型
1.1.1 概念
可选类型(Optional):表示一个值可能存在或不存在的类型
可选类型通过在类型名后添加问号
?来表示可选类型的可能值:
一个具体的值(如
Int类型的42)nil(表示值不存在)
1.1.2 原理
Swift引入可选类型是为了解决空指针问题,这是许多其他语言(如Java、C++)中常见的崩溃原因。通过明确标注可选类型,Swift编译器可以在编译时检查可选值的使用,确保开发者正确处理值可能不存在的情况。
1.1.3 使用示例
// 声明可选类型
var optionalInt: Int?
var optionalString: String? = "Hello"
var optionalDouble: Double? = nil
// 检查可选值是否存在
if optionalInt != nil {
print("optionalInt has a value: (optionalInt!)")
} else {
print("optionalInt is nil")
}
// 强制解包(只有确定可选值存在时才能使用)
// let unwrappedInt = optionalInt! // 运行时错误,因为optionalInt是nil
let unwrappedString = optionalString! // 安全,因为optionalString有值
print(unwrappedString) // 输出: Hello1.2 可选绑定
1.2.1 概念
可选绑定(Optional Binding)是一种安全地访问可选值的方式,它可以检查可选值是否存在,并将其解包赋值给一个临时变量或常量。
1.2.2 原理
可选绑定避免了强制解包可能导致的运行时错误,它通过if、guard、for-in等语句来实现。如果可选值存在,则解包并执行相应的代码块;否则,跳过代码块。
1.2.3 使用示例
// if let 可选绑定
var optionalName: String? = "Alice"
if let name = optionalName {
print("Hello, (name)") // 输出: Hello, Alice
} else {
print("Hello, unknown")
}
// 多个可选绑定
var optionalAge: Int? = 25
if let name = optionalName, let age = optionalAge {
print("(name) is (age) years old") // 输出: Alice is 25 years old
}
// 带条件的可选绑定
if let name = optionalName, let age = optionalAge, age >= 18 {
print("(name) is an adult") // 输出: Alice is an adult
}
// guard let 可选绑定(用于提前退出)
func greet(person: [String: String]) {
guard let name = person["name"] else {
print("No name provided")
return
}
print("Hello, \(name)!")
}
let user = ["name": "Bob", "age": "30"]
greet(person: user) // 输出: Hello, Bob!
let anonymousUser = ["age": "25"]
greet(person: anonymousUser) // 输出: No name provided1.3 可选链
1.3.1 概念
可选链(Optional Chaining)是一种在可选值上调用属性、方法和下标脚本的方式,如果可选值为nil,则整个表达式的值为nil。
1.3.2 原理
可选链允许我们在一条链中访问多个可选值的属性或方法,而不需要为每个可选值单独进行可选绑定。如果链中的任何一个环节为nil,则整个链的结果为nil,不会导致运行时错误。
1.3.3 使用示例
// 定义类结构
class Person {
var residence: Residence?
}
class Residence {
var rooms: [Room] = []
var numberOfRooms: Int {
return rooms.count
}
}
class Room {
let name: String
init(name: String) {
self.name = name
}
}
// 创建实例
let john = Person()
// 可选链调用属性
if let roomCount = john.residence?.numberOfRooms {
print("John’s residence has (roomCount) rooms.")
} else {
print("Unable to retrieve the number of rooms.") // 输出: Unable to retrieve the number of rooms.
}
// 可选链调用方法
john.residence?.printNumberOfRooms() // 无输出,因为residence是nil
// 可选链调用下标
if let firstRoomName = john.residence?[0].name {
print("The first room name is (firstRoomName).")
} else {
print("Unable to retrieve the first room name.") // 输出: Unable to retrieve the first room name.
}
// 赋值给可选链
john.residence?[0] = Room(name: "Bathroom") // 无效果,因为residence是nil
// 设置residence后再使用可选链
let someResidence = Residence()
someResidence.rooms.append(Room(name: "Living Room"))
someResidence.rooms.append(Room(name: "Kitchen"))
john.residence = someResidence
if let roomCount = john.residence?.numberOfRooms {
print("John’s residence has (roomCount) rooms.") // 输出: John’s residence has 2 rooms.
}1.4 错误处理
1.4.1 概念
错误处理(Error Handling)是一种处理程序执行过程中可能出现的错误的机制。Swift提供了抛出、捕获、传递和操作可恢复错误的功能。
1.4.2 原理
Swift的错误处理基于抛出、捕获和处理错误的思想。错误用符合Error协议的类型表示,通过throw关键字抛出错误,通过do-try-catch语句捕获和处理错误。
1.4.3 使用示例
// 定义错误类型
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
// 模拟自动售货机
struct Item {
let price: Int
let count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
// 使用do-try-catch处理错误
let vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try vendingMachine.vend(itemNamed: "Candy Bar")
} catch VendingMachineError.invalidSelection {
print("Invalid selection.")
} catch VendingMachineError.outOfStock {
print("Out of stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional (coinsNeeded) coins.") // 输出: Insufficient funds. Please insert an additional 4 coins.
} catch {
print("Unexpected error: (error).")
}
// 传播错误
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let favoriteSnack = favoriteSnack(person: person)
try vendingMachine.vend(itemNamed: favoriteSnack)
}
func favoriteSnack(person: String) -> String {
switch person {
case "Alice": return "Chips"
case "Bob": return "Pretzels"
default: return "Candy Bar"
}
}
// try? 和 try! 表达式
let snackName = "Chips"
vendingMachine.coinsDeposited = 10
// try? 返回可选值,如果出错则返回nil
if let result = try? vendingMachine.vend(itemNamed: snackName) {
print("Successfully vended (snackName)") // 输出: Dispensing Chips
} else {
print("Failed to vend (snackName)")
}
// try! 强制解包,如果出错则崩溃(只有确定不会出错时才能使用)
vendingMachine.coinsDeposited = 10
try! vendingMachine.vend(itemNamed: "Chips") // 输出: Dispensing Chips
2. 内存管理
2.1 自动引用计数(ARC)
2.1.1 概念
自动引用计数(Automatic Reference Counting,ARC)是Swift用于管理内存的机制,它会自动跟踪和管理对象的内存使用。
2.1.2 原理
当创建一个对象时,ARC会分配一块内存来存储该对象的信息
ARC会跟踪有多少强引用指向该对象
当强引用计数变为0时,ARC会释放该对象占用的内存
ARC只管理引用类型的内存,值类型由编译器直接管理
2.1.3 使用示例
// 定义类
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
// 创建强引用
var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John") // 输出: John is being initialized
// 强引用计数: 1
reference2 = reference1
// 强引用计数: 2
reference3 = reference1
// 强引用计数: 3
// 释放强引用
reference1 = nil
// 强引用计数: 2
reference2 = nil
// 强引用计数: 1
reference3 = nil
// 强引用计数: 0,对象被释放
// 输出: John is being deinitialized
2.2 强引用、弱引用、无主引用
2.2.1 概念
强引用(Strong Reference):默认的引用类型,会增加对象的引用计数
弱引用(Weak Reference):不会增加对象的引用计数,对象释放后会自动变为
nil,使用weak关键字声明无主引用(Unowned Reference):不会增加对象的引用计数,假设对象永远不会被释放,使用
unowned关键字声明
2.2.2 原理
弱引用和无主引用用于解决循环引用问题。弱引用适用于引用的对象可能为nil的情况,无主引用适用于引用的对象永远不会为nil的情况。
2.2.3 使用示例
// 弱引用示例
class Apartment {
let unit: String
weak var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) is being initialized")
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John") // 输出: John is being initialized
unit4A = Apartment(unit: "4A") // 输出: Apartment 4A is being initialized
john?.apartment = unit4A
unit4A?.tenant = john
john = nil // 输出: John is being deinitialized
// 此时,Person对象被释放,但Apartment对象仍存在
unit4A = nil // 输出: Apartment 4A is being deinitialized
// 现在,Apartment对象也被释放
// 无主引用示例
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
print("Customer \(name) is being initialized")
}
deinit {
print("Customer \(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
print("Credit card \(number) is being initialized")
}
deinit {
print("Credit card \(number) is being deinitialized")
}
}
var bob: Customer?
bob = Customer(name: "Bob") // 输出: Customer Bob is being initialized
bob?.card = CreditCard(number: 1234_5678_9012_3456, customer: bob!) // 输出: Credit card 1234567890123456 is being initialized
bob = nil // 输出: Customer Bob is being deinitialized 和 Credit card 1234567890123456 is being deinitialized
// 两个对象都被释放,因为CreditCard对Customer的引用是无主引用
2.3 闭包与循环引用
2.3.1 概念
当闭包捕获了其外部作用域中的变量或常量时,可能会导致循环引用。这是因为闭包默认会对其捕获的对象创建强引用。
2.3.2 原理
为了解决闭包导致的循环引用,Swift提供了捕获列表(Capture List),用于指定闭包对捕获对象的引用类型(强引用、弱引用或无主引用)。
2.3.3 使用示例
// 闭包导致的循环引用
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
print("HTMLElement \(name) is being initialized")
}
deinit {
print("HTMLElement \(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "Hello, world!")
print(paragraph?.asHTML() ?? "") // 输出: <p>Hello, world!</p>
paragraph = nil // 没有输出,因为存在循环引用,对象没有被释放
// 使用弱引用解决循环引用
class HTMLElement2 {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[weak self] in // 捕获列表,使用弱引用
guard let self = self else { // 检查self是否存在
return ""
}
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
print("HTMLElement2 \(name) is being initialized")
}
deinit {
print("HTMLElement2 \(name) is being deinitialized")
}
}
var paragraph2: HTMLElement2? = HTMLElement2(name: "p", text: "Hello, world!")
print(paragraph2?.asHTML() ?? "") // 输出: <p>Hello, world!</p>
paragraph2 = nil // 输出: HTMLElement2 p is being deinitialized,对象被正确释放
// 使用无主引用解决循环引用(当self永远不会为nil时)
class HTMLElement3 {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[unowned self] in // 捕获列表,使用无主引用
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
print("HTMLElement3 \(name) is being initialized")
}
deinit {
print("HTMLElement3 \(name) is being deinitialized")
}
}
var paragraph3: HTMLElement3? = HTMLElement3(name: "p", text: "Hello, world!")
print(paragraph3?.asHTML() ?? "") // 输出: <p>Hello, world!</p>
paragraph3 = nil // 输出: HTMLElement3 p is being deinitialized,对象被正确释放
3. 高级类型
3.1 枚举高级用法
3.1.1 概念
Swift的枚举(Enumeration)是一种灵活的类型,可以包含关联值和原始值,还可以定义方法和属性。
3.1.2 原理
枚举的高级用法包括:
关联值:为枚举成员添加额外的数据
原始值:为枚举成员指定默认值
方法和属性:为枚举添加实例方法和类型方法
协议遵守:让枚举遵守协议
3.1.3 使用示例
// 关联值
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
// 匹配关联值
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: (numberSystem), (manufacturer), (product), (check).")
case .qrCode(let productCode):
print("QR code: (productCode).")
}
// 原始值
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
// 隐式原始值
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
let earthsOrder = Planet.earth.rawValue // 输出: 3
let mars = Planet(rawValue: 4) // 输出: Optional(Planet.mars)
// 枚举方法
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
func evaluate() -> Int {
switch self {
case .number(let value):
return value
case .addition(let left, let right):
return left.evaluate() + right.evaluate()
case .multiplication(let left, let right):
return left.evaluate() * right.evaluate()
}
}
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(product.evaluate()) // 输出: (5+4)*2 = 18
3.2 关联类型
3.2.1 概念
关联类型(Associated Type)是在协议中定义的一种占位符类型,用于指定协议中方法或属性的类型,具体类型由遵守协议的类型来确定。
3.2.2 原理
关联类型允许协议在不指定具体类型的情况下定义通用的接口,实现了泛型协议的功能。遵守协议的类型需要为关联类型指定具体的类型。
3.2.3 使用示例
// 定义带有关联类型的协议
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
// 结构体遵守协议,指定关联类型
struct IntStack: Container {
// 原始实现
var items = Int
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// 遵守Container协议
typealias ItemType = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
// 泛型类型遵守协议
struct Stack<Element>: Container {
// 原始实现
var items = Element
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// 遵守Container协议,Swift会自动推断ItemType为Element
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
// 扩展现有类型以遵守协议
extension Array: Container {
// Array已经实现了append、count和subscript,所以只需要指定ItemType
// Swift会自动推断ItemType为Array.Element
}
3.3 类型擦除
3.3.1 概念
类型擦除(Type Erasure)是一种将具体类型的信息隐藏起来,只暴露必要接口的技术。它允许我们使用不同的具体类型,但通过相同的抽象接口来操作它们。
3.3.2 原理
在Swift中,当我们需要处理多种具体类型但又不想暴露它们的具体类型时,可以使用类型擦除。常见的实现方式是创建一个包装器类型,该类型内部持有具体类型的实例,但对外只暴露协议定义的接口。
3.3.3 使用示例
// 定义协议
protocol Animal {
associatedtype Food
func eat(_ food: Food)
func sleep()
}
// 具体类型1
class Dog: Animal {
typealias Food = Bone
func eat(_ food: Bone) {
print("Dog is eating \(food.type)")
}
func sleep() {
print("Dog is sleeping")
}
}
struct Bone {
let type: String
}
// 具体类型2
class Cat: Animal {
typealias Food = Fish
func eat(_ food: Fish) {
print("Cat is eating \(food.type)")
}
func sleep() {
print("Cat is sleeping")
}
}
struct Fish {
let type: String
}
// 类型擦除包装器
class AnyAnimal<F>: Animal {
private let _eat: (F) -> Void
private let _sleep: () -> Void
init<A: Animal>(_ animal: A) where A.Food == F {
self._eat = animal.eat
self._sleep = animal.sleep
}
func eat(_ food: F) {
_eat(food)
}
func sleep() {
_sleep()
}
}
// 使用类型擦除
let dog = AnyAnimal(Dog())
dog.sleep() // 输出: Dog is sleeping
let cat = AnyAnimal(Cat())
cat.sleep() // 输出: Cat is sleeping
// 注意:由于类型擦除后,Food类型被固定,所以不能混合不同Food类型的Animal
// 可以创建一个更通用的类型擦除,或者使用不同的Food类型分别处理
3.4 元类型
3.4.1 概念
元类型(Meta Type)是指类型本身,而不是类型的实例。在Swift中,元类型使用Type关键字表示,如Int.Type表示Int类型本身。
3.4.2 原理
元类型用于:
获取类型的信息
动态创建实例
访问类型的静态属性和方法
3.4.3 使用示例
// 获取元类型
let intType: Int.Type = Int.self
let stringType: String.Type = String.self
// 使用元类型创建实例
class Person {
var name: String
required init(name: String) {
self.name = name
}
static func createInstance(name: String) -> Self {
return self.init(name: name)
}
func introduce() {
print("Hello, my name is \(name)")
}
}
let personType: Person.Type = Person.self
let person = personType.init(name: "Alice")
person.introduce() // 输出: Hello, my name is Alice
// 类型约束
func createInstance<T: Person>(of type: T.Type, name: String) -> T {
return type.init(name: name)
}
let bob = createInstance(of: Person.self, name: "Bob")
bob.introduce() // 输出: Hello, my name is Bob
// 元类型作为函数参数
func printTypeInfo(_ type: Any.Type) {
print("Type: (type)")
print("Type name: (String(describing: type))")
}
printTypeInfo(Int.self) // 输出: Type: Int
// Type name: Int
printTypeInfo(Person.self) // 输出: Type: Person
// Type name: Person
4. 总结
Swift的高级特性包括可选类型与错误处理、内存管理和高级类型等,这些特性共同构成了Swift强大的表达能力和安全性:
可选类型与错误处理:提供了安全的方式来处理可能不存在的值和可能发生的错误,避免了运行时崩溃
内存管理:通过ARC自动管理内存,同时提供了弱引用、无主引用和捕获列表来解决循环引用问题
高级类型:包括枚举的关联值和原始值、关联类型、类型擦除和元类型等,使Swift类型系统更加灵活和强大
掌握这些高级特性对于编写高质量的Swift代码至关重要,它们可以帮助你编写更加安全、高效和易于维护的代码。
5. 练习
定义一个
Result枚举,用于表示操作的成功或失败,并实现相应的错误处理。创建一个类层次结构,演示强引用、弱引用和无主引用的使用,并观察对象的生命周期。
编写一个闭包,使用捕获列表来避免循环引用。
创建一个带有关联值的枚举,用于表示不同类型的网络请求,并实现一个处理函数。
定义一个带有关联类型的协议,然后让多个类型遵守该协议,并使用类型擦除来统一处理这些类型。
通过这些练习,你可以巩固所学的Swift高级特性知识,提高自己的Swift编程能力。