Swift 开发教程:从基础语法到项目实践
Swift 是苹果公司于 2014 年 WWDC(苹果开发者大会)发布的一款强大且直观的编程语言,专为构建 iOS、macOS、watchOS 和 tvOS 应用而设计。它融合了 C 和 Objective-C 的精华,并在此基础上进行了大量现代化改进,注重安全性、性能和开发效率。本教程将带你从 Swift 的基础语法开始,逐步过渡到实际的项目实践,助你开启 Swift 开发之旅。
第一部分:Swift 基础语法
1. 环境搭建与初识 Swift
- Xcode: Swift 开发的主要集成开发环境(IDE)是 Xcode。你可以从 Mac App Store 免费下载并安装它。Xcode 包含了编写、编译、调试 Swift 代码所需的一切工具,包括 Swift 编译器、Interface Builder(用于 UI 设计)以及性能分析工具。
- Playgrounds: Xcode 提供了一个名为 Playgrounds 的交互式环境,非常适合学习 Swift 语法和实验代码片段。创建一个新的 Playground (File > New > Playground),选择一个模板(如 Blank),你就可以立即开始编写和运行 Swift 代码了。
2. 变量与常量
在 Swift 中,我们使用 let
来声明常量(值一旦设定就不能更改),使用 var
来声明变量(值可以后续更改)。
“`swift
// 常量
let maximumNumberOfLoginAttempts = 10
let welcomeMessage = “Hello, Swift!”
// 变量
var currentLoginAttempt = 0
var currentTemperature = 25.5
var greeting = “Hi”
greeting = “Bonjour” // 变量的值可以修改
// Swift 是类型安全的语言,编译器会自动推断类型
let inferredInteger = 70 // 推断为 Int
let inferredDouble = 70.0 // 推断为 Double
var inferredString = “This is a string” // 推断为 String
// 也可以显式声明类型
let explicitDouble: Double = 70
var explicitString: String = “Another string”
``
let`,以增强代码的可预测性和安全性。
Swift 推荐优先使用
3. 数据类型
Swift 提供了丰富的数据类型,主要包括:
* Int: 整数类型,例如 10
, -5
。
* Double / Float: 浮点数类型,用于表示小数。Double
精度更高(64位),Float
为 32 位。通常推荐使用 Double
。
* Bool: 布尔类型,只有两个值:true
和 false
。
* String: 字符串类型,用于表示文本数据,例如 "Hello"
。
* Character: 单个字符类型,例如 'A'
(用双引号表示,如 "A"
)。
swift
let anInteger: Int = 100
let aDouble: Double = 3.14159
let aBool: Bool = true
let aString: String = "Swift is fun!"
let aCharacter: Character = "🚀" // Unicode 字符也支持
4. 运算符
Swift 支持常见的算术运算符、比较运算符、逻辑运算符等。
* 算术运算符: +
(加), -
(减), *
(乘), /
(除), %
(取余)。
swift
let sum = 10 + 5 // 15
let difference = 10 - 5 // 5
let product = 10 * 5 // 50
let quotient = 10.0 / 4.0 // 2.5 (注意整数相除会截断小数部分)
let remainder = 9 % 4 // 1
* 比较运算符: ==
(等于), !=
(不等于), >
(大于), <
(小于), >=
(大于等于), <=
(小于等于)。
swift
1 == 1 // true
2 != 1 // true
2 > 1 // true
* 逻辑运算符: !
(逻辑非), &&
(逻辑与), ||
(逻辑或)。
swift
let isSunny = true
let isWarm = false
if isSunny && isWarm {
print("Perfect weather!")
} else if isSunny || isWarm {
print("It's okay.")
} else {
print("Not so good.")
}
* 区间运算符:
* 闭区间运算符: a...b
(包含 a 和 b)
* 半开区间运算符: a..<b
(包含 a,不包含 b)
“`swift
for index in 1…5 {
print(“(index) times 5 is (index * 5)”)
}
// 输出:
// 1 times 5 is 5
// 2 times 5 is 10
// …
// 5 times 5 is 25
let names = ["Anna", "Alex", "Brian", "Jack"]
for i in 0..<names.count { // 0, 1, 2, 3
print("Person \(i + 1) is called \(names[i])")
}
```
5. 集合类型
Swift 提供了三种主要的集合类型:数组(Array)、字典(Dictionary)和集合(Set)。
-
Array: 有序的值的集合。
“`swift
// 创建一个空数组
var someInts: [Int] = []
print(“someInts is of type [Int] with (someInts.count) items.”)// 创建一个带有默认值的数组
var threeDoubles = Array(repeating: 0.0, count: 3) // [0.0, 0.0, 0.0]// 使用字面量创建数组
var shoppingList: [String] = [“Eggs”, “Milk”]
// shoppingList.append(“Flour”)
// shoppingList += [“Baking Powder”]
// shoppingList[0] = “Six eggs”
// print(shoppingList[1]) // Milk
* **Set**: 无序的唯一值的集合。
swift
var favoriteGenres: Set= [“Rock”, “Classical”, “Hip hop”]
// favoriteGenres.insert(“Jazz”)
// if favoriteGenres.contains(“Funk”) { print(“I love Funk!”) }
* **Dictionary**: 无序的键值对集合。键必须是唯一的,并且是可哈希的(通常是 String, Int 等)。
swift
// 创建一个空字典
var namesOfIntegers: [Int: String] = [:]// 使用字面量创建字典
var airports: [String: String] = [“YYZ”: “Toronto Pearson”, “DUB”: “Dublin”]
// airports[“LHR”] = “London Heathrow”
// airports[“YYZ”] = “Toronto Pearson International” // 更新值
// let airportName = airports[“DUB”] // “Dublin”
// airports[“APL”] = nil // 移除键值对
“`
6. 控制流
-
条件语句 (if, else if, else, switch)
“`swift
let temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
print(“It’s very cold. Consider wearing a scarf.”)
} else if temperatureInFahrenheit >= 86 {
print(“It’s really warm. Don’t forget to wear sunscreen.”)
} else {
print(“It’s not that cold. Wear a T-shirt.”)
}let someCharacter: Character = “z”
switch someCharacter {
case “a”, “e”, “i”, “o”, “u”:
print(“(someCharacter) is a vowel”)
case “b”, “c”, “d”, “f”, “g”, “h”, “j”, “k”, “l”, “m”,
“n”, “p”, “q”, “r”, “s”, “t”, “v”, “w”, “x”, “y”, “z”:
print(“(someCharacter) is a consonant”)
default:
print(“(someCharacter) is not a vowel or a consonant”)
}
// Swift 的 switch 语句默认不会贯穿 (fallthrough),每个 case 结束后会自动 break。
// case 还可以匹配区间和元组。
let approximateCount = 62
let countedThings = “moons orbiting Saturn”
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = “no”
case 1..<5:
naturalCount = “a few”
case 5..<12:
naturalCount = “several”
case 12..<100:
naturalCount = “dozens of”
default:
naturalCount = “many”
}
print(“There are (naturalCount) (countedThings).”) // “There are dozens of moons orbiting Saturn.”
“` -
循环语句 (for-in, while, repeat-while)
“`swift
// for-in 循环
let names = [“Anna”, “Alex”, “Brian”, “Jack”]
for name in names {
print(“Hello, (name)!”)
}let numberOfLegs = [“spider”: 8, “ant”: 6, “cat”: 4]
for (animalName, legCount) in numberOfLegs {
print(“(animalName)s have (legCount) legs”)
}// while 循环
var n = 2
while n < 100 {
n *= 2
}
print(n) // 128// repeat-while 循环 (至少执行一次)
var m = 2
repeat {
m *= 2
} while m < 100
print(m) // 128
“`
7. 函数 (Functions)
函数是执行特定任务的独立代码块。
“`swift
// 定义函数
func greet(person: String) -> String {
let greeting = “Hello, ” + person + “!”
return greeting
}
print(greet(person: “Dave”)) // “Hello, Dave!”
// 无参数,无返回值的函数
func sayHelloWorld() {
print(“Hello, world!”)
}
sayHelloWorld()
// 多参数函数
func addNumbers(num1: Int, num2: Int) -> Int {
return num1 + num2
}
let result = addNumbers(num1: 5, num2: 3) // 8
// 参数标签和参数名称
// 默认情况下,函数参数使用其参数名称作为其参数标签。
// 你可以指定外部参数名(参数标签)和内部参数名。
func greetAgain(person name: String, from hometown: String) -> String {
return “Hello (name)! Glad you could visit from (hometown).”
}
print(greetAgain(person: “Bill”, from: “Cupertino”))
// 省略参数标签
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// 在函数体内,firstParameterName 和 secondParameterName 指向参数的实际值
}
someFunction(1, secondParameterName: 2) // 第一个参数标签被省略
//默认参数值
func someFunctionWithDefault(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// 如果你在调用时候不给 parameterWithDefault 传值,它会使用默认值 12
}
someFunctionWithDefault(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunctionWithDefault(parameterWithoutDefault: 4) // parameterWithDefault = 12
// 可变参数 (Variadic Parameters)
func arithmeticMean(_ numbers: Double…) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
print(arithmeticMean(1, 2, 3, 4, 5)) // 3.0
print(arithmeticMean(3, 8.25, 18.75)) // 10.0
// 输入输出参数 (In-Out Parameters)
// 如果你希望函数能够修改传入参数的值,并且这些修改在函数调用结束后仍然存在,
// 那么你应该把这个参数定义为输入输出参数。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt) // 注意调用时需要 & 符号
print(“someInt is now (someInt), and anotherInt is now (anotherInt)”) // someInt is now 107, and anotherInt is now 3
“`
8. 可选类型 (Optionals)
可选类型用于处理值可能缺失的情况。一个可选类型要么包含一个值,要么是 nil
(表示没有值)。
“`swift
var optionalString: String? = “Hello”
print(optionalString == nil) // false
var optionalName: String? // 自动初始化为 nil
// optionalName = “John Appleseed”
// 强制解包 (Forced Unwrapping) – 如果可选值为 nil,会触发运行时错误,慎用!
// if optionalName != nil {
// print(“Hello, (optionalName!)”)
// }
// 可选绑定 (Optional Binding) – 更安全的方式
if let name = optionalName {
print(“Hello, (name)”) // 只有当 optionalName 不为 nil 时执行
} else {
print(“No name provided.”)
}
// 隐式解包可选类型 (Implicitly Unwrapped Optionals)
// 当你确定一个可选类型在第一次赋值后就一直会有值时使用。主要用于类初始化。
let assumedString: String! = “An implicitly unwrapped optional string.”
let implicitString: String = assumedString // 不需要感叹号
// nil 合并运算符 (Nil-Coalescing Operator) a ?? b
// 如果 a 不是 nil,则解包 a;如果 a 是 nil,则返回 b。
let defaultColorName = “red”
var userDefinedColorName: String? // 默认为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName // “red”
userDefinedColorName = “green”
colorNameToUse = userDefinedColorName ?? defaultColorName // “green”
“`
9. 结构体 (Structures) 与 类 (Classes)
结构体和类是创建自定义数据类型的蓝图。
* 共同点:
* 定义属性来存储值。
* 定义方法来提供功能。
* 定义下标脚本以使用下标语法访问值。
* 定义构造器以设置初始状态。
* 可以通过扩展来增加功能。
* 可以遵循协议来提供标准功能。
* 类的额外功能:
* 继承:允许一个类继承另一个类的特征。
* 类型转换:允许在运行时检查和解释一个类实例的类型。
* 析构器:允许一个类实例释放任何其所被分配的资源。
* 引用计数:允许多个对一个类实例的引用。(类是引用类型)
* 结构体是值类型 (Value Types): 当它们被赋值给一个常量或变量,或者被传递给一个函数时,它们的值会被复制。
* 类是引用类型 (Reference Types): 当它们被赋值给一个常量或变量时,实际上传递的是对内存中同一实例的引用,而不是复制。
“`swift
// 结构体定义
struct Resolution {
var width = 0
var height = 0
func description() -> String {
return "Resolution: \(width)x\(height)"
}
}
// 类定义
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String? // 可选属性
init(name: String?, resolution: Resolution, frameRate: Double) { // 自定义构造器
self.name = name
self.resolution = resolution
self.frameRate = frameRate
}
// 如果所有属性都有默认值,且没有自定义构造器,会自动生成默认构造器
// 如果类是 NSObject 的子类,或者所有属性都是可选的,或者都有默认值,会提供一个默认的无参构造器。
func description() -> String {
return "VideoMode: \(name ?? "Unnamed"), \(resolution.description()), \(frameRate) fps, \(interlaced ? "interlaced" : "progressive")"
}
}
// 结构体实例 (值类型)
var hd = Resolution(width: 1920, height: 1080)
var cinema = hd // cinema 是 hd 的一个副本
cinema.width = 2048
print(hd.width) // 1920 (hd 未改变)
print(cinema.width) // 2048
// 类实例 (引用类型)
let tenEighty = VideoMode(name: “1080p”, resolution: hd, frameRate: 25.0)
let alsoTenEighty = tenEighty // alsoTenEighty 和 tenEighty 指向同一个实例
alsoTenEighty.frameRate = 30.0
print(tenEighty.frameRate) // 30.0 (tenEighty 的 frameRate 也改变了)
“`
选择结构体还是类:一般情况下,如果数据模型比较简单,不需要继承,且希望在传递时复制值,则使用结构体。对于更复杂的对象,需要继承或引用行为时,使用类。苹果推荐优先考虑结构体。
10. 属性 (Properties)
- 存储属性 (Stored Properties): 存储常量或变量作为实例的一部分 (只能用于类和结构体)。
- 计算属性 (Computed Properties): 不直接存储值,而是提供一个 getter 和一个可选的 setter 来间接获取和设置其他属性或值 (可用于类、结构体和枚举)。
swift
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point { // 计算属性
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) { // 可选的 setter
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
// 如果只有 getter,可以简写:
// var center: Point {
// Point(x: origin.x + (size.width / 2), y: origin.y + (size.height / 2))
// }
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center // {x 5.0, y 5.0}
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))") // (10.0, 10.0)
* 属性观察器 (Property Observers): willSet
和 didSet
,用于响应属性值的变化 (除了懒加载存储属性和计算属性)。
swift
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200 // About to set totalSteps to 200; Added 200 steps
stepCounter.totalSteps = 360 // About to set totalSteps to 360; Added 160 steps
11. 枚举 (Enumerations)
枚举为一组相关值定义了一个共同的类型。
“`swift
enum CompassPoint {
case north
case south
case east
case west
}
var directionToHead = CompassPoint.west
directionToHead = .east // 类型已知,可以省略枚举名
// 关联值 (Associated Values)
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).”)
}
// 原始值 (Raw Values) – 必须是唯一且类型相同
enum ASCIIControlCharacter: Character {
case tab = “\t”
case lineFeed = “\n”
case carriageReturn = “\r”
}
let tabChar = ASCIIControlCharacter.tab.rawValue // “\t”
enum Planet: Int { // 隐式原始值,从0开始
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
// mercury is 1, venus is 2, …
}
let earthsOrder = Planet.earth.rawValue // 3
let possiblePlanet = Planet(rawValue: 7) // uranus (可选类型)
“`
12. 协议 (Protocols)
协议定义了一个蓝图,规定了某些方法、属性等规范,供类、结构体或枚举来遵循。
“`swift
protocol FullyNamed {
var fullName: String { get } // 可读属性
func describe() -> String // 方法
}
struct Person: FullyNamed {
var firstName: String
var lastName: String
var fullName: String { // 计算属性实现
return “(firstName) (lastName)”
}
func describe() -> String {
return “Person: (fullName)”
}
}
let john = Person(firstName: “John”, lastName: “Doe”)
print(john.fullName) // John Doe
print(john.describe()) // Person: John Doe
“`
13. 扩展 (Extensions)
扩展可以为已有的类、结构体、枚举或协议类型添加新功能。
“`swift
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print(“One inch is (oneInch) meters”) // One inch is 0.0254 meters
let threeFeet = 3.ft
print(“Three feet is (threeFeet) meters”) // Three feet is 0.914399970739201 meters
// 扩展可以添加计算属性、方法、构造器、下标、嵌套类型,以及使现有类型遵循某个协议。
“`
14. 错误处理 (Error Handling)
Swift 提供了强大的错误处理机制,使用 try
, catch
, throw
和 throws
。
“`swift
enum VendingMachineError: Error { // 错误类型必须遵循 Error 协议
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
struct Item {
var price: Int
var 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)")
}
}
let favoriteSnacks = [
“Alice”: “Chips”,
“Bob”: “Licorice”, // 无此商品
“Eve”: “Pretzels”,
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? “Candy Bar”
try vendingMachine.vend(itemNamed: snackName)
}
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 100
// 使用 do-catch 处理错误
do {
try buyFavoriteSnack(person: “Alice”, vendingMachine: vendingMachine)
print(“Alice got her snack.”)
try buyFavoriteSnack(person: “Bob”, vendingMachine: vendingMachine) // 这会抛出错误
print(“Bob got his snack.”) // 这行不会执行
} 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.”)
} catch { // 通用 catch 块
print(“An unexpected error occurred: (error)”)
}
// 输出:
// Dispensing Chips
// Alice got her snack.
// Invalid Selection.
// try? 将可抛出函数的结果转换为可选值,如果抛出错误则为 nil
let resultTryOptional = try? buyFavoriteSnack(person: “Eve”, vendingMachine: vendingMachine)
if resultTryOptional != nil {
print(“Eve got her snack using try?”)
}
// try! 强制解包可抛出函数的结果,如果抛出错误则程序崩溃(慎用)
// try! buyFavoriteSnack(person: “Carl”, vendingMachine: vendingMachine) // 如果 Carl 不存在或机器有问题,会崩溃
“`
15. 闭包 (Closures)
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的 blocks 以及其他语言中的 lambda 表达式类似。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用,这被称为闭合并包裹那些常量和变量。
“`swift
let names = [“Chris”, “Alex”, “Ewa”, “Barry”, “Daniella”]
// 完整闭包表达式语法
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames) // [“Ewa”, “Daniella”, “Chris”, “Barry”, “Alex”]
// 根据上下文推断类型
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
// 单表达式闭包隐式返回
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
// 参数名称缩写
reversedNames = names.sorted(by: { $0 > $1 } )
// 运算符方法 (如果类型定义了 해당运算符的实现)
reversedNames = names.sorted(by: >)
// 尾随闭包 (Trailing Closures)
// 如果函数最后一个参数是闭包,可以将闭包写在函数括号之后。
reversedNames = names.sorted() { $0 > $1 }
// 如果闭包是函数唯一参数,可以省略括号
reversedNames = names.sorted { $0 > $1 }
// 逃逸闭包 (@escaping)
// 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,
// 我们称该闭包从函数中逃逸。定义函数时需要用 @escaping 标记。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
“`
第二部分:Swift 进阶与面向对象
1. 继承 (Inheritance)
类可以继承另一个类的方法、属性和其他特性。被继承的类称为父类(superclass),继承的类称为子类(subclass)。
“`swift
class Vehicle {
var currentSpeed = 0.0
var description: String {
return “traveling at (currentSpeed) miles per hour”
}
func makeNoise() {
// 什么也不做,因为并非所有车辆都会发出声音
}
}
class Bicycle: Vehicle { // Bicycle 继承自 Vehicle
var hasBasket = false
}
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print(“Bicycle: (bicycle.description)”) // Bicycle: traveling at 15.0 miles per hour
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print(“Tandem: (tandem.description)”)
// 重写 (Overriding)
class Train: Vehicle {
override func makeNoise() { // 使用 override 关键字
print(“Choo Choo”)
}
}
let train = Train()
train.makeNoise() // Choo Choo
“`
2. 类型转换 (Type Casting)
- is: 检查实例是否属于特定的子类型。
- as?: 向下转型为可选类型,如果转型失败则返回
nil
。 - as!: 强制向下转型,如果转型失败则触发运行时错误。
“`swift
let library = [
Movie(name: “Casablanca”, director: “Michael Curtiz”),
Song(name: “Blue Suede Shoes”, artist: “Elvis Presley”),
Movie(name: “Citizen Kane”, director: “Orson Welles”),
Song(name: “The One And Only”, artist: “Chesney Hawkes”)
] // 假设 Movie 和 Song 都是 MediaItem 的子类
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print(“Media library contains (movieCount) movies and (songCount) songs”)
for item in library {
if let movie = item as? Movie { // 安全的向下转型
print(“Movie: (movie.name), dir. (movie.director)”)
} else if let song = item as? Song {
print(“Song: (song.name), by (song.artist)”)
}
}
“`
3. ARC (Automatic Reference Counting)
Swift 使用自动引用计数(ARC)来管理应用的内存。ARC 会自动跟踪和管理类实例的引用。当一个实例不再被任何强引用指向时,ARC 会自动释放它占用的内存。
* 强引用循环 (Strong Reference Cycles): 如果两个类实例相互持有对方的强引用,会导致它们都无法被释放。
* 解决强引用循环: 使用弱引用 (weak
) 或无主引用 (unowned
)。
* weak
: 当引用的实例可能变为 nil
时使用。弱引用必须是可选类型变量。
* unowned
: 当引用的实例生命周期确定不会在其宿主之前结束时使用。无主引用总是非可选的。
“`swift
class PersonARC {
let name: String
init(name: String) { self.name = name }
var apartment: ApartmentARC?
deinit { print(“(name) is being deinitialized”) }
}
class ApartmentARC {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: PersonARC? // 使用 weak 解决循环引用
// unowned var tenant: PersonARC! // 如果确定 tenant 总是有值
deinit { print(“Apartment (unit) is being deinitialized”) }
}
var johnArc: PersonARC? = PersonARC(name: “John Appleseed”)
var unit4A: ApartmentARC? = ApartmentARC(unit: “4A”)
johnArc?.apartment = unit4A
unit4A?.tenant = johnArc // 建立相互引用
johnArc = nil // PersonARC 实例的强引用计数变为 1 (来自 unit4A.tenant)
unit4A = nil // ApartmentARC 实例的强引用计数变为 0,被释放。释放后,其对 PersonARC 的弱引用 tenant 失效,PersonARC 实例的强引用计数变为 0,也被释放。
// 如果 tenant 是强引用,两个实例都不会被释放。
“`
4. 现代并发 (Concurrency with async/await)
Swift 5.5 引入了内置的并发模型,使用 async
和 await
关键字简化异步代码的编写。
“`swift
// 模拟网络请求函数
func fetchWeatherData() async -> String {
// 模拟网络延迟
try? await Task.sleep(nanoseconds: 1_000_000_000) // 暂停1秒
return “Sunny, 25°C”
}
func fetchNews() async -> String {
try? await Task.sleep(nanoseconds: 1_500_000_000) // 暂停1.5秒
return “Swift 6.0 Released!”
}
// 串行执行
func loadDashboardSerial() async {
print(“Loading dashboard serially…”)
let weather = await fetchWeatherData()
print(“Weather: (weather)”)
let news = await fetchNews()
print(“News: (news)”)
print(“Dashboard loaded serially.”)
}
// 并行执行
func loadDashboardParallel() async {
print(“Loading dashboard in parallel…”)
async let weather = fetchWeatherData() // 开始异步任务,但不等待
async let news = fetchNews() // 开始另一个异步任务
// 等待两个任务的结果
let weatherResult = await weather
let newsResult = await news
print("Weather: \(weatherResult)")
print("News: \(newsResult)")
print("Dashboard loaded in parallel.")
}
// 在非 async 上下文调用 async 函数,需要 Task
Task {
await loadDashboardSerial()
print(“—“)
await loadDashboardParallel()
}
“`
第三部分:项目实践 – 构建一个简单的待办事项 App (概念)
虽然这里无法完整展示一个带 UI 的 App,但我们可以勾勒出其核心逻辑和数据结构。我们将使用 SwiftUI(苹果现代化的声明式 UI 框架)作为 UI 层面的设想。
1. 项目规划
- 功能:
- 显示待办事项列表。
- 添加新的待办事项。
- 标记待办事项为已完成/未完成。
- 删除待办事项。
- (可选) 数据持久化(如使用
UserDefaults
,Core Data
, orSwiftData
)。
2. 数据模型 (Model)
定义一个结构体来表示单个待办事项。
“`swift
import SwiftUI // 通常在 SwiftUI 文件中
struct TodoItem: Identifiable, Codable { // Codable 用于数据持久化
let id: UUID // Identifiable 协议要求,用于列表唯一标识
var task: String
var isCompleted: Bool
init(id: UUID = UUID(), task: String, isCompleted: Bool = false) {
self.id = id
self.task = task
self.isCompleted = isCompleted
}
}
“`
3. 视图模型 (ViewModel) / 数据管理器
创建一个类来管理待办事项的数组,并提供增删改查的逻辑。这个类将遵循 ObservableObject
协议,以便 SwiftUI 视图可以观察其变化并自动更新。
“`swift
class TodoManager: ObservableObject {
@Published var todoItems: [TodoItem] = [] // @Published 会在数据改变时通知视图
init() {
// (可选) 加载已保存的数据
loadItems()
}
func addItem(task: String) {
let newItem = TodoItem(task: task)
todoItems.append(newItem)
// (可选) 保存数据
// saveItems()
}
func toggleCompletion(for item: TodoItem) {
if let index = todoItems.firstIndex(where: { $0.id == item.id }) {
todoItems[index].isCompleted.toggle()
// (可选) 保存数据
// saveItems()
}
}
func deleteItem(at offsets: IndexSet) { // SwiftUI List 的 onDelete 修饰符提供 IndexSet
todoItems.remove(atOffsets: offsets)
// (可选) 保存数据
// saveItems()
}
// 简单的 UserDefaults 持久化示例 (可以替换为 Core Data 或 SwiftData)
private func saveItems() {
if let encoded = try? JSONEncoder().encode(todoItems) {
UserDefaults.standard.set(encoded, forKey: "todoItems")
}
}
private func loadItems() {
if let savedItems = UserDefaults.standard.data(forKey: "todoItems") {
if let decodedItems = try? JSONDecoder().decode([TodoItem].self, from: savedItems) {
todoItems = decodedItems
return
}
}
// 如果没有保存的数据,可以加载一些示例数据
// todoItems = [
// TodoItem(task: "Buy groceries"),
// TodoItem(task: "Read a book", isCompleted: true),
// TodoItem(task: "Go for a run")
// ]
}
}
“`
4. 视图 (View – SwiftUI)
使用 SwiftUI 构建用户界面。
-
ContentView.swift (主视图)
“`swift
import SwiftUIstruct ContentView: View {
@StateObject var todoManager = TodoManager() // 观察 TodoManager 的变化
@State private var newTask: String = “” // 用于 TextField 输入var body: some View { NavigationView { VStack { HStack { TextField("Enter new task...", text: $newTask) .textFieldStyle(RoundedBorderTextFieldStyle()) Button(action: { if !newTask.isEmpty { todoManager.addItem(task: newTask) newTask = "" // 清空输入框 } }) { Image(systemName: "plus.circle.fill") .font(.title2) } } .padding() List { ForEach(todoManager.todoItems) { item in TodoRow(item: item, todoManager: todoManager) } .onDelete(perform: todoManager.deleteItem) // 滑动删除 } } .navigationTitle("My Todos") .toolbar { // 添加编辑按钮,用于批量删除等操作 EditButton() } } }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
“` -
TodoRow.swift (列表行视图)
“`swift
import SwiftUIstruct TodoRow: View {
// 使用 @ObservedObject 或直接传递 item,并让 manager 更新
// 这里我们选择直接传递 item,并通过 manager 的方法来更新它
// 如果 TodoItem 是类且遵循 ObservableObject,可以使用 @ObservedObject var item: TodoItem
// 但因为它是结构体,我们修改它时会创建一个新的副本,所以通过 manager 来操作
let item: TodoItem
@ObservedObject var todoManager: TodoManager // 保持对 manager 的引用以调用方法var body: some View { HStack { Image(systemName: item.isCompleted ? "checkmark.circle.fill" : "circle") .foregroundColor(item.isCompleted ? .green : .gray) .onTapGesture { todoManager.toggleCompletion(for: item) } Text(item.task) .strikethrough(item.isCompleted, color: .gray) // 完成时添加删除线 Spacer() // 将内容推到左边 } }
}
“`
5. 运行与调试
在 Xcode 中选择一个模拟器或连接一个真实设备,点击运行按钮。你可以使用 Xcode 的调试器来设置断点、检查变量值等,以帮助排查问题。
6. 进一步扩展
- UI 改进: 添加更多视觉元素、动画。
- 数据持久化: 使用 Core Data 或 SwiftData 实现更健壮的数据存储。
- 分类/标签: 允许用户为待办事项添加分类或标签。
- 提醒功能: 集成本地通知。
- 云同步: 使用 iCloud (CloudKit) 实现多设备同步。
第四部分:学习资源与后续步骤
- 官方文档:
- The Swift Programming Language: Apple 官方的 Swift 语言指南,是最权威、最全面的学习资料。可在 Apple Books 或官网上找到。
- SwiftUI Tutorials: Apple 提供的 SwiftUI 官方教程,通过实际项目学习 SwiftUI。
- 在线课程:
- 100 Days of SwiftUI (by Paul Hudson – HackingWithSwift.com): 非常受欢迎的免费 SwiftUI 学习课程。
- Stanford CS193p: 斯坦福大学的 iOS 开发课程(通常使用 SwiftUI),视频和材料免费公开。
- Coursera, Udemy, RayWenderlich.com (Kodeco) 等平台也有大量优质 Swift 和 iOS 开发课程。
- 社区与博客:
- Hacking with Swift: Paul Hudson 的网站,包含大量 Swift 和 SwiftUI 教程、文章和新闻。
- Swift.org: Swift 官方网站,包含博客、社区论坛链接等。
- Stack Overflow: 提问和查找 Swift 相关问题的好地方。
- 动手实践:
- 不断编写代码,尝试实现自己的小项目。
- 参与开源项目。
- 阅读优秀的 Swift 开源代码。
结论
Swift 是一门现代、安全且强大的编程语言,学习它将为你打开 iOS、macOS 及其他苹果平台应用开发的大门。从掌握基础语法开始,逐步理解其核心概念如可选类型、结构体与类、协议和错误处理,然后通过 async/await
学习现代并发编程。最后,通过实际项目(如 SwiftUI 应用)将所学知识融会贯通。
编程学习是一个持续的过程,保持好奇心,不断实践,勇于挑战更复杂的项目,你就能在 Swift 开发的道路上越走越远。祝你学习愉快!