Apple Swift 编程入门指南


Apple Swift 编程入门指南:开启你的 iOS 与 macOS 开发之旅

引言:拥抱现代、强大且直观的编程语言

在当今数字化的世界里,移动应用和桌面软件已成为我们生活和工作中不可或缺的一部分。而在这背后,强大的编程语言驱动着创新。Apple 公司于 2014 年全球开发者大会 (WWDC) 上推出的 Swift 语言,正是这样一颗耀眼的新星。它凭借其现代化的设计、卓越的性能、高度的安全性以及相对平易近人的语法,迅速成为开发 iOS, iPadOS, macOS, watchOS 和 tvOS 应用的首选语言,并逐渐在服务器端、机器学习等领域崭露头角。

对于希望进入 Apple 生态系统开发的初学者,或者寻求掌握一门新技能的开发者来说,学习 Swift 无疑是一个明智的选择。本指南旨在为你提供一个全面而详细的 Swift 编程入门路线图,从基础概念到核心特性,帮助你稳步踏上 Swift 开发之路。这趟旅程需要耐心和实践,但 Swift 的优雅和强大一定会让你觉得不虚此行。

第一章:准备启程 —— 环境搭建与初识 Swift

1.1 为何选择 Swift?

在深入学习之前,我们先简单了解 Swift 的核心优势:

  • 现代 (Modern): Swift 吸收了许多现代编程语言的优秀特性,如类型推断、泛型、闭包、元组等,使得代码更简洁、表达力更强。
  • 安全 (Safe): Swift 在设计上极其注重安全性。例如,它的可选类型 (Optionals) 机制强制开发者处理值可能缺失 (nil) 的情况,大大减少了运行时因空指针引起的崩溃。变量在使用前必须初始化,数组越界等常见错误也更容易被捕获。
  • 快速 (Fast): Swift 使用高性能的 LLVM 编译器进行编译,其性能目标是与 C++ 和 Objective-C 相媲美,甚至在某些场景下超越它们。
  • 交互性强 (Interactive): Xcode 中的 Playgrounds 功能允许你实时看到 Swift 代码的运行结果,这对于学习、实验和快速原型设计非常有帮助。
  • 开源 (Open Source): Swift 是一门开源语言,拥有活跃的社区支持,你可以在 swift.org 上找到它的源代码、开发动态和社区资源。这意味着它不仅限于 Apple 平台,也可以在 Linux 等其他系统上运行。
  • 与 Objective-C 共存: Swift 可以与 Apple 平台长期使用的 Objective-C 语言无缝协作,允许在同一个项目中使用两种语言,方便逐步迁移旧项目或利用现有的 Objective-C 库。

1.2 开发环境:Xcode 与 Playgrounds

学习 Swift 最主要的工具是 Apple 官方提供的集成开发环境 (IDE) —— Xcode

  • 安装 Xcode: 你可以从 Mac App Store 免费下载并安装最新版本的 Xcode。安装过程可能需要较长时间,因为它包含了完整的 macOS 和 iOS SDK (软件开发工具包) 以及各种开发工具。
  • 初识 Xcode: 打开 Xcode 后,你会看到一个欢迎界面。对于初学者,最常接触的两个选项是:
    • Get started with a Playground: 这是学习 Swift 语法的理想之地。Playground 提供了一个交互式的环境,你编写的每一行代码都会立即执行,并在侧边栏显示结果。这极大地降低了学习门槛,让你能快速实验和验证想法。
    • Create a new Xcode project: 当你准备开始构建实际的应用程序时,就需要创建项目了。Xcode 提供了多种项目模板,如 App, Framework, Library 等。
  • 使用 Playgrounds: 强烈建议初学者从 Playground 开始。创建一个新的 Playground (File > New > Playground...),选择一个模板 (通常 Blank 就足够了),然后就可以开始编写你的第一行 Swift 代码了。

第二章:Swift 基础语法 —— 构建代码的基石

2.1 基本概念

  • 注释 (Comments): 用于解释代码,编译器会忽略它们。
    • 单行注释:// 这是一行注释
    • 多行注释:/* 这是\n多行注释 */
  • 分号 (Semicolons): Swift 不强制要求在每行代码末尾加分号。只有当你想在同一行写多条语句时才需要用分号隔开。
  • 标识符 (Identifiers): 用于命名常量、变量、函数、类型等的名称。可以包含 Unicode 字符,但不能以数字开头,不能包含空格、数学符号、箭头等,且不能与 Swift 关键字冲突。

2.2 常量 (Constants) 与变量 (Variables)

这是编程中最基本的概念,用于存储数据。

  • 常量 (let): 一旦赋值后,其值就不能再改变。推荐优先使用常量,除非你明确知道值需要改变,这有助于提高代码的安全性和可预测性。
    let maximumLoginAttempts = 10
    let welcomeMessage = "Hello, Swift!"
  • 变量 (var): 其值可以在声明后被修改。
    var currentLoginAttempt = 0
    var score = 100
    score = 110 // 合法

2.3 类型推断 (Type Inference) 与显式类型 (Explicit Types)

Swift 是一门强类型语言,意味着每个常量或变量都有一个明确的类型。但 Swift 拥有强大的类型推断能力,编译器通常能根据你赋的初始值自动推断出类型。

  • 类型推断:
    let pi = 3.14159 // Swift 推断 pi 为 Double 类型
    var message = "Loading..." // Swift 推断 message 为 String 类型
  • 显式类型: 如果编译器无法推断,或者你希望明确指定类型,可以使用冒号 (:) 后跟类型名称的方式。
    let explicitDouble: Double = 70.0
    var playerHealth: Int = 100

2.4 常见数据类型 (Common Data Types)

  • Int: 整数类型,用于表示没有小数部分的数字。根据平台 (32位或64位),它的大小会不同 (通常是 Int64)。
    let age: Int = 30
  • Double & Float: 浮点数类型,用于表示带小数的数字。Double 精度更高 (64位),Float 精度较低 (32位)。通常推荐使用 Double
    let averageScore: Double = 88.5
    let temperature: Float = 23.4
  • Bool: 布尔类型,只有两个可能的值:true (真) 和 false (假)。常用于条件判断。
    let isLoggedIn: Bool = true
    var hasFinished: Bool = false
  • String: 字符串类型,用于表示文本数据。使用双引号 (") 包裹。
    let greeting: String = "Welcome to Swift!"
    let multilineString = """
    This is a string
    that spans multiple
    lines.
    """

    字符串支持拼接 (+) 和插值 (\()):
    let firstName = "Taylor"
    let lastName = "Swift"
    let fullName = firstName + " " + lastName // 拼接
    let profile = "Name: \(fullName), Age: \(age)" // 插值,更推荐
  • Character: 单个字符类型。
    let initial: Character = "S"
  • 元组 (Tuple): 将多个不同类型的值组合成一个复合值。元组的值可以是命名的,也可以通过索引访问。
    let httpStatus = (404, "Not Found")
    print("Status code: \(httpStatus.0)")
    print("Status message: \(httpStatus.1)")

    let namedTuple = (code: 200, message: "OK")
    print("Code: \(namedTuple.code)")

2.5 运算符 (Operators)

Swift 支持大多数标准 C 语言的运算符,并进行了一些改进。

  • 赋值运算符 (=): 将右侧的值赋给左侧的变量/常量。let b = 10; var a = 5; a = b
  • 算术运算符 (+, -, *, /, %): 加、减、乘、除、取余。注意 Swift 不允许数值类型溢出 (除非使用特定的溢出运算符)。整数除法会舍弃小数部分。
    let sum = 5 + 3 // 8
    let difference = 10 - 4 // 6
    let product = 2 * 6 // 12
    let quotient = 10 / 3 // 3
    let remainder = 10 % 3 // 1
  • 复合赋值运算符 (+=, -=, *=, /=, %=): a += 2 等价于 a = a + 2
  • 比较运算符 (==, !=, >, <, >=, <=): 返回一个布尔值 (truefalse)。
    1 == 1 // true
    2 != 1 // true
    2 > 1 // true
  • 逻辑运算符 (!, &&, ||): 逻辑非 (NOT)、逻辑与 (AND)、逻辑或 (OR)。
    let isRaining = true
    let isCold = false
    if !isCold { print("It's not cold.") }
    if isRaining && isCold { print("Wear a coat and take an umbrella.") }
    if isRaining || isCold { print("Weather might be unpleasant.") }
  • 范围运算符 (Range Operators):
    • 闭区间 (a...b): 包含 a 和 b。for i in 1...5 { print(i) } // 输出 1 2 3 4 5
    • 半开区间 (a..<b): 包含 a,但不包含 b。for i in 1..<5 { print(i) } // 输出 1 2 3 4
    • 单侧区间 (a..., ...b, ..<b): 用于数组切片等。
  • 三元条件运算符 (a ? b : c): 如果 atrue,则表达式的值为 b,否则为 c
    let contentHeight = 40
    let rowHeight = contentHeight + (hasHeader ? 50 : 20)
  • 空合运算符 (a ?? b): 用于处理可选类型 (后面会讲)。如果 a 不是 nil,则解包 a 并返回其值;如果 anil,则返回默认值 bb 的类型必须与 a 解包后的类型匹配。
    let defaultColorName = "red"
    var userDefinedColorName: String? // 可选类型,初始为 nil
    let colorNameToUse = userDefinedColorName ?? defaultColorName // "red"

第三章:集合类型 (Collection Types)

用于存储一组值的容器。Swift 提供了三种主要的集合类型:数组 (Array)、字典 (Dictionary) 和集合 (Set)。它们都是泛型的,可以存储特定类型的值。

3.1 数组 (Array)

有序的值的集合。同一个数组中的元素类型必须相同。

  • 创建数组:
    var emptyIntArray: [Int] = []
    var shoppingList: [String] = ["Eggs", "Milk"]
    var scores = [98, 85, 92] // 类型推断为 [Int]
    let fixedSizeArray = Array(repeating: 0.0, count: 3) // [0.0, 0.0, 0.0]
  • 访问和修改:
    print("First item: \(shoppingList[0])") // "Eggs"
    shoppingList[0] = "Six eggs"
    shoppingList.append("Flour")
    shoppingList += ["Baking Powder"]
    shoppingList.insert("Chocolate Spread", at: 0)
    let removedItem = shoppingList.remove(at: 1)
    print("Number of items: \(shoppingList.count)")
    if shoppingList.isEmpty { ... }
  • 遍历数组:
    for item in shoppingList { print(item) }
    for (index, value) in shoppingList.enumerated() { print("Item \(index + 1): \(value)") }

3.2 字典 (Dictionary)

无序的键值对 (key-value pair) 集合。每个键 (key) 必须是唯一的,并且类型必须是可哈希的 (如 String, Int, Double, Bool 等)。值 (value) 的类型必须相同。

  • 创建字典:
    var emptyStringDict: [String: String] = [:]
    var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
    var responseCodes = [200: "OK", 404: "Not Found"] // 推断为 [Int: String]
  • 访问和修改:
    print("Airport name for YYZ: \(airports["YYZ"]!)") // 注意:字典查找返回可选类型,这里强制解包 (不推荐,后面讲)
    if let airportName = airports["DUB"] { print("Airport name: \(airportName)") } // 安全的方式
    airports["LHR"] = "London Heathrow"
    airports["YYZ"] = "Toronto Pearson International" // 更新值
    airports["DUB"] = nil // 删除键值对
    if let removedValue = airports.removeValue(forKey: "LHR") { ... }
    print("Number of airports: \(airports.count)")
  • 遍历字典:
    for (airportCode, airportName) in airports { print("\(airportCode): \(airportName)") }
    for airportCode in airports.keys { print("Code: \(airportCode)") }
    for airportName in airports.values { print("Name: \(airportName)") }

3.3 集合 (Set)

无序的、唯一值的集合。同一个集合中的元素类型必须相同且可哈希。主要用于快速判断一个元素是否存在,以及执行集合运算 (交集、并集、差集等)。

  • 创建集合:
    var emptyLetterSet: Set<Character> = []
    var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
    // 注意:不能直接用字面量推断 Set,会默认为 Array
    // var numbers = {1, 2, 3} // 错误
    var oddNumbers: Set = [1, 3, 5, 7, 9] // 显式声明类型
  • 访问和修改:
    favoriteGenres.insert("Jazz")
    if favoriteGenres.contains("Funk") { ... }
    if let removedGenre = favoriteGenres.remove("Rock") { ... }
    print("Number of genres: \(favoriteGenres.count)")
  • 集合运算:
    let evenNumbers: Set = [0, 2, 4, 6, 8]
    let primeNumbers: Set = [2, 3, 5, 7]
    let unionSet = oddNumbers.union(evenNumbers).sorted() // 并集 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    let intersectionSet = oddNumbers.intersection(primeNumbers).sorted() // 交集 [3, 5, 7]
    let differenceSet = oddNumbers.subtracting(primeNumbers).sorted() // 差集 [1, 9]
    let symmetricDiffSet = oddNumbers.symmetricDifference(primeNumbers).sorted() // 对称差集 [1, 2, 9]
  • 遍历集合:
    for genre in favoriteGenres.sorted() { print(genre) } // 排序后遍历

第四章:控制流 (Control Flow)

控制代码执行顺序的结构。

4.1 条件语句 (Conditional Statements)

  • if, else if, else: 根据条件执行不同的代码块。
    let temperatureInCelsius = 30
    if temperatureInCelsius <= 0 {
    print("It's freezing!")
    } else if temperatureInCelsius < 15 {
    print("It's cold.")
    } else if temperatureInCelsius < 25 {
    print("It's warm.")
    } else {
    print("It's hot!")
    }
  • switch: 对一个值进行多种可能情况的匹配。Swift 的 switch 非常强大:

    • 不需要 break:默认情况下,匹配到一个 case 后就会自动退出 switch 语句 (不会贯穿 Fallthrough)。如果需要贯穿,需显式使用 fallthrough 关键字。
    • 必须是详尽的 (Exhaustive): 必须覆盖所有可能的值,或者提供一个 default 分支。
    • 可以匹配多种类型:不仅限于整数和字符串,可以匹配任何类型的值。
    • 可以匹配区间、元组、进行值绑定 (Value Binding) 和使用 where 子句添加额外条件。

    let someCharacter: Character = "z"
    switch someCharacter {
    case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
    case "b", "c", ..., "z": // 可以使用范围
    print("\(someCharacter) is a consonant")
    default:
    print("\(someCharacter) is not a letter")
    }

    let approximateCount = 62
    switch approximateCount {
    case 0:
    naturalCount = "no"
    case 1..<5:
    naturalCount = "a few"
    case 5..<12:
    naturalCount = "several"
    case 12..<100:
    naturalCount = "dozens of"
    case 100..<1000:
    naturalCount = "hundreds of"
    default:
    naturalCount = "many"
    }

    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
    print("Origin")
    case (_, 0): // 匹配 x 轴上的点
    print("On the x-axis with x = \(somePoint.0)")
    case (0, _): // 匹配 y 轴上的点
    print("On the y-axis with y = \(somePoint.1)")
    case (-2...2, -2...2): // 匹配区域内的点
    print("Inside the box")
    default:
    print("Outside of the box")
    }

    // 值绑定与 where 子句
    let anotherPoint = (2, 0)
    switch anotherPoint {
    case (let x, 0): // 绑定 x
    print("On the x-axis with an x value of \(x)")
    case (0, let y): // 绑定 y
    print("On the y-axis with a y value of \(y)")
    case let (x, y) where x == y: // 绑定 x, y 并添加条件
    print("On the line x == y at (\(x), \(y))")
    case let (x, y): // 绑定 x, y (捕获所有其他情况)
    print("Somewhere else at (\(x), \(y))")
    }

4.2 循环语句 (Looping Statements)

  • 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") }

    for index in 1...5 { print("\(index) times 5 is \(index * 5)") }
    * while: 当条件为 true 时,重复执行代码块。先判断条件,再执行。
    var counter = 5
    while counter > 0 {
    print("Countdown: \(counter)")
    counter -= 1
    }
    * repeat-while: 先执行一次代码块,然后当条件为 true 时,重复执行。至少执行一次。
    var diceRoll = 0
    repeat {
    diceRoll = Int.random(in: 1...6)
    print("You rolled a \(diceRoll)")
    } while diceRoll != 6
    print("You rolled a 6!")

4.3 控制转移语句 (Control Transfer Statements)

  • continue: 跳过当前循环的剩余部分,直接开始下一次迭代。
  • break: 立即终止整个循环语句或 switch 语句的执行。
  • fallthrough:switch 语句中,强制执行下一个 case 的代码块 (不常用)。
  • return: 从函数或方法中返回值并退出。
  • throw: 抛出一个错误 (用于错误处理)。
  • 标签语句 (Labeled Statements): 可以给循环或 switch 语句添加标签,breakcontinue 可以指定跳转到哪个标签标记的语句。

第五章:函数 (Functions)

组织可重用代码块的基本方式。

5.1 定义与调用函数

func functionName(parameterName: ParameterType) -> ReturnType {
// 函数体
// ...
return value // 如果有返回值
}

  • 无参数,无返回值:
    func sayHello() {
    print("Hello!")
    }
    sayHello()
  • 有参数,无返回值:
    func greet(person: String) {
    print("Hello, \(person)!")
    }
    greet(person: "Dave")
  • 有参数,有返回值:
    func addTwoInts(_ a: Int, _ b: Int) -> Int { // 使用 _ 省略外部参数名
    return a + b
    }
    let sum = addTwoInts(5, 3)
  • 无参数,有返回值:
    func getRandomNumber() -> Int {
    return Int.random(in: 1...100)
    }
    let randomNumber = getRandomNumber()
  • 多返回值 (使用元组):
    func findMinMax(array: [Int]) -> (min: Int, max: Int)? { // 返回可选元组
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
    if value < currentMin { currentMin = value }
    else if value > currentMax { currentMax = value }
    }
    return (currentMin, currentMax)
    }
    if let bounds = findMinMax(array: [8, -6, 2, 109, 3, 71]) {
    print("Min is \(bounds.min) and max is \(bounds.max)")
    }

5.2 参数标签 (Argument Labels) 和参数名 (Parameter Names)

  • 参数标签: 调用函数时使用的名称。
  • 参数名: 函数内部使用的名称。
  • 默认情况下,参数标签和参数名相同。
  • 可以指定不同的参数标签和参数名:func someFunction(argumentLabel parameterName: Type)
  • 可以使用下划线 (_) 省略参数标签:func someFunction(_ parameterName: Type)
  • 第一个参数默认省略参数标签 (除非显式提供)。

    func greet(person name: String, from hometown: String) {
    print("Hello \(name)! Glad you could visit from \(hometown).")
    }
    greet(person: "Bill", from: "Cupertino") // 调用时使用参数标签 person 和 from

5.3 默认参数值 (Default Parameter Values)

可以为参数提供默认值,调用时可以省略该参数。

func power(base: Int, exponent: Int = 2) -> Int { ... }
power(base: 3) // 9 (exponent 使用默认值 2)
power(base: 2, exponent: 3) // 8

5.4 可变参数 (Variadic Parameters)

允许函数接受零个或多个指定类型的值。参数名后加 ...。函数内部会将这些值作为一个数组来访问。一个函数最多只能有一个可变参数。

func average(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers { total += number }
return numbers.isEmpty ? 0 : total / Double(numbers.count)
}
average(1, 2, 3, 4, 5) // 3.0
average(3, 8.25, 18.75) // 10.0

5.5 输入输出参数 (In-Out Parameters)

函数默认不能修改传入的参数值 (值传递)。如果希望函数能修改参数的值,并且这些修改在函数调用结束后仍然生效,可以将参数声明为 inout。调用时,需要传递变量,并在变量名前加上 &

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)") // 输出 107 和 3

5.6 函数类型 (Function Types)

每个函数都有特定的函数类型,由参数类型和返回类型组成。可以将函数赋值给变量或常量,也可以作为参数传递给其他函数,或作为函数的返回值。

func add(_ a: Int, _ b: Int) -> Int { return a + b }
func multiply(_ a: Int, _ b: Int) -> Int { return a * b }

var mathFunction: (Int, Int) -> Int = add // mathFunction 的类型是 (Int, Int) -> Int
print("Result: \(mathFunction(2, 3))") // 输出 5

mathFunction = multiply
print("Result: \(mathFunction(2, 3))") // 输出 6

func printMathResult(_ operation: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(operation(a, b))")
}
printMathResult(add, 5, 3) // 输出 8

第六章:可选类型 (Optionals) —— 处理值缺失的艺术

这是 Swift 最核心和最具特色的功能之一,用于处理值可能不存在 (为 nil) 的情况。nil 在 Swift 中表示“没有值”,它不是指针,而是一个特殊的值。

6.1 可选类型的声明

在类型名称后加上问号 (?) 来声明一个可选类型。

var optionalString: String? = "Hello"
var optionalInt: Int? // 默认值为 nil

6.2 为何需要可选类型?

很多操作可能会失败并返回“没有值”,例如:
* 字典查找:airports["XYZ"] 可能找不到对应的机场名。
* 类型转换:Int("hello") 无法将字符串 "hello" 转换为整数。
* 访问可能为空的属性或调用可能失败的方法。

可选类型强制你思考并处理这些 nil 的情况,防止在运行时因访问 nil 而导致程序崩溃 (这是 Objective-C 和许多其他语言中常见的错误来源)。

6.3 解包可选类型 (Unwrapping Optionals)

要使用可选类型中包含的值,你需要先“解包”(unwrap) 它。有几种安全的方式:

  • 可选绑定 (if let / guard let): 最安全、最常用的方式。尝试解包可选值,如果成功 (值不是 nil),则将解包后的值赋给一个临时常量或变量,并在 ifguard 的代码块中使用它。
    var serverResponseCode: Int? = 404
    if let code = serverResponseCode {
    print("Received response code: \(code)") // 只有当 serverResponseCode 不是 nil 时执行
    } else {
    print("No response code received.")
    }

    func process(code: Int) { print("Processing code \(code)...") }
    guard let validCode = serverResponseCode else {
    print("Cannot process: Invalid response code.")
    // guard 语句的 else 分支必须退出当前作用域 (如 return, break, throw)
    return
    }
    // 在 guard 语句之后,validCode 就可以在当前作用域的剩余部分使用了
    process(code: validCode)

  • 空合运算符 (??): 提供一个默认值,当可选值为 nil 时使用。
    let userColorPreference: String? = nil
    let colorToDisplay = userColorPreference ?? "DefaultBlue"
    print(colorToDisplay) // "DefaultBlue"

  • 可选链 (?.): 如果你想调用可选值的属性、方法或下标,但不想在每次调用前都写 if let,可以使用可选链。如果可选值为 nil,整个调用链会优雅地失败并返回 nil,而不会崩溃。如果可选值非 nil,则调用成功,返回结果仍然是可选类型 (因为链中任何一步都可能返回 nil)。
    class Person {
    var residence: Residence?
    }
    class Residence {
    var numberOfRooms = 1
    }
    let john = Person()
    // let roomCount = john.residence!.numberOfRooms // 危险!如果 residence 是 nil 会崩溃
    if let roomCount = john.residence?.numberOfRooms { // 使用可选链
    print("John's residence has \(roomCount) room(s).")
    } else {
    print("Unable to retrieve the number of rooms.") // 因为 john.residence 是 nil
    }
    john.residence = Residence()
    if let roomCount = john.residence?.numberOfRooms {
    print("John's residence now has \(roomCount) room(s).") // 输出 1
    }

  • 强制解包 (!): 在可选类型变量/常量名后加感叹号 (!)。这会直接访问可选值,但 如果此时可选值为 nil,程序会立即崩溃! 只有在你 绝对确定 可选值不可能为 nil 的情况下才应使用它。通常应避免使用。
    let assumedString: String! = "An implicitly unwrapped optional string." // 隐式解包可选类型
    let implicitString: String = assumedString // 不需要解包,但如果 assumedString 是 nil 也会崩溃

    let possibleString: String? = "An optional string."
    let forcedString: String = possibleString! // 强制解包,如果 possibleString 为 nil 则崩溃

第七章:结构体 (Structures) 与类 (Classes)

Swift 中创建自定义数据类型的两种主要方式。它们有很多相似之处 (定义属性、方法、下标、构造器、扩展、遵循协议),但也有关键区别。

7.1 共同点

  • 定义属性 (Properties) 来存储值。
  • 定义方法 (Methods) 来提供功能。
  • 定义下标 (Subscripts) 来通过下标语法访问值。
  • 定义构造器 (Initializers) 来设置初始状态。
  • 可以通过扩展 (Extensions) 来增加功能。
  • 可以遵循协议 (Protocols) 来提供标准功能。

7.2 定义语法

struct Resolution {
var width = 0
var height = 0
}

class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}

7.3 实例创建

使用构造器语法创建实例。

let someResolution = Resolution() // 使用默认构造器
let someVideoMode = VideoMode() // 使用默认构造器

let vga = Resolution(width: 640, height: 480) // 使用成员构造器 (结构体自动获得)

7.4 属性访问

使用点语法 (.) 访问实例的属性。

print("The width of someResolution is \(someResolution.width)") // 0
someVideoMode.resolution.width = 1280

7.5 关键区别:值类型 (Value Types) vs 引用类型 (Reference Types)

  • 结构体 (Struct) 和枚举 (Enum) 是值类型: 当它们被赋值给新的常量或变量,或者作为参数传递给函数时,它们的值会被 复制。每个实例都拥有自己独立的数据副本。
    var hd = Resolution(width: 1920, height: 1080)
    var cinema = hd // cinema 得到的是 hd 的一个副本
    cinema.width = 2048
    print("hd width is still \(hd.width)") // 输出 1920 (hd 未受影响)
  • 类 (Class) 是引用类型: 当它们被赋值给新的常量或变量,或者作为参数传递给函数时,传递的是对 同一个 已存在实例的 引用 (或称为“指针”)。多个常量或变量可以指向同一个类的实例。修改其中一个引用的实例会影响到所有指向该实例的引用。
    let tenEighty = VideoMode()
    tenEighty.resolution = hd
    tenEighty.interlaced = true
    tenEighty.name = "1080i"
    tenEighty.frameRate = 25.0

    let alsoTenEighty = tenEighty // alsoTenEighty 指向与 tenEighty 相同的 VideoMode 实例
    alsoTenEighty.frameRate = 30.0
    print("The frameRate property of tenEighty is now \(tenEighty.frameRate)") // 输出 30.0

  • 恒等运算符 (===, !==): 用于判断两个类类型的常量或变量是否引用同一个实例。
    if tenEighty === alsoTenEighty { print("Referencing the same VideoMode instance.") }

7.6 何时选择结构体,何时选择类?

Apple 的一般建议是:优先使用结构体

  • 使用结构体的情况:
    • 主要目的是封装少量相关简单数据值。
    • 期望实例在赋值或传递时被复制,而不是共享引用。
    • 不需要继承其他类的属性或行为。
    • 实例的数据不需要在多个地方同步改变。
      (例如:坐标点、尺寸、颜色、配置信息等)
  • 使用类的情况:
    • 需要使用 Objective-C 的互操作性。
    • 需要控制实例的身份 (使用 === 判断是否为同一对象)。
    • 需要创建继承层次结构。
    • 实例需要在多个地方共享并同步状态。
      (例如:视图控制器、网络请求管理器、数据模型对象如果需要在多处共享状态)

第八章:属性 (Properties)

关联到特定类、结构体或枚举的值。

  • 存储属性 (Stored Properties): 存储常量或变量作为实例的一部分。只能用于类和结构体。
    struct FixedLengthRange {
    var firstValue: Int // 存储属性
    let length: Int // 常量存储属性
    }
  • 计算属性 (Computed Properties): 不直接存储值,而是提供 getter 和可选的 setter 来间接获取和设置其他属性或值。可以用于类、结构体和枚举。
    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 { // getter
    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,可以省略 get {},称为只读计算属性
    // var area: Double { return size.width * size.height }
    }
    }
  • 属性观察器 (Property Observers): 监控和响应属性值的变化。可以添加到自定义的存储属性上 (非 lazy),以及从父类继承来的存储属性和计算属性上。
    • willSet: 在新值被存储之前调用。新值作为常量参数传入 (默认名 newValue)。
    • didSet: 在新值被存储后立即调用。旧值作为参数传入 (默认名 oldValue)。
      class StepCounter {
      var totalSteps: Int = 0 {
      willSet(newTotalSteps) {
      print("About to set totalSteps to \(newTotalSteps)")
      }
      didSet {
      if totalSteps > oldValue {
      print("Added \(totalSteps - oldValue) steps")
      }
      }
      }
      }
  • 类型属性 (Type Properties): 属于类型本身,而不是类型的任何一个实例。使用 static 关键字定义 (对于类,也可以用 class 关键字来允许子类重写计算类型属性)。
    struct AudioChannel {
    static let thresholdLevel = 10 // 静态存储属性
    static var maxInputLevelForAllChannels = 0 // 静态变量存储属性
    var currentLevel: Int = 0 { ... }
    }
    AudioChannel.maxInputLevelForAllChannels = 5 // 通过类型名访问

第九章:方法 (Methods)

与特定类型关联的函数。

  • 实例方法 (Instance Methods): 属于类、结构体或枚举的实例。需要先创建实例才能调用。可以访问和修改实例属性,调用其他实例方法。self 关键字用于引用当前实例 (通常可以省略)。
    class Counter {
    var count = 0
    func increment() { self.count += 1 }
    func increment(by amount: Int) { count += amount }
    func reset() { count = 0 }
    }
    let counter = Counter()
    counter.increment()
    counter.increment(by: 5)
  • 值类型中的可变方法 (Mutating Methods): 默认情况下,值类型 (结构体、枚举) 的实例方法不能修改实例的属性。如果需要修改,必须在 func 关键字前加上 mutating
    struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
    x += deltaX
    y += deltaY
    // 甚至可以给 self 赋一个全新的实例// self = Point(x: x + deltaX, y: y + deltaY)}}var somePoint = Point(x: 1.0, y: 1.0)somePoint.moveBy(x: 2.0, y: 3.0)`
  • 类型方法 (Type Methods): 属于类型本身,而不是实例。使用 static 关键字定义 (对于类,也可以用 class 关键字来允许子类重写)。通过类型名调用。
    class SomeClass {
    class func someTypeMethod() {
    // 类型方法实现
    print("Called type method")
    }
    }
    SomeClass.someTypeMethod()

第十章:初步探索更深层次

掌握了以上基础后,你已经具备了编写简单 Swift 程序的能力。接下来可以开始探索一些更高级但同样重要的概念:

  • 构造过程 (Initialization): 类和结构体在创建实例时如何设置初始状态 (init 方法)。
  • 析构过程 (Deinitialization): 类实例在被销毁前如何执行清理工作 (deinit 方法)。
  • 继承 (Inheritance): (仅限类) 一个类如何获得另一个类 (父类) 的属性和方法。
  • 类型转换 (Type Casting): 检查实例的类型 (is),或者将其向下转型 (as?, as!) 为其子类类型。
  • 协议 (Protocols): 定义一套方法、属性或其他要求的蓝图,类、结构体或枚举可以遵循 (conform to) 这个协议来实现这些要求。类似其他语言的接口 (Interface)。
  • 扩展 (Extensions): 为已有的类、结构体、枚举或协议类型添加新功能 (方法、计算属性、构造器、协议遵循等),即使你没有原始源代码。
  • 错误处理 (Error Handling): 使用 throw, throws, do-catch, try?, try! 来处理程序中可能发生的错误情况。
  • 并发 (Concurrency): 使用 async/await 等现代 Swift 特性来编写异步代码,处理耗时任务而不会阻塞主线程。
  • 泛型 (Generics): 编写灵活、可重用的函数和类型,它们可以适用于任何类型,同时保持类型安全。
  • 内存管理 (Memory Management): Swift 使用自动引用计数 (ARC) 来管理内存。了解 ARC 的基本原理以及如何解决循环引用问题 (使用 weakunowned 引用) 对于编写高效且无内存泄漏的应用至关重要。

第十一章:下一步:构建应用

学习了 Swift 语言本身后,下一步就是将其应用于实际的应用程序开发。这通常涉及到学习 Apple 提供的 UI 框架:

  • SwiftUI: Apple 最新的声明式 UI 框架,用于为所有 Apple 平台构建用户界面。它使用更现代、更简洁的 Swift 语法,并提供实时预览功能。对于新项目和初学者来说,这是一个很好的选择。
  • UIKit: 成熟的、命令式的 UI 框架,长期用于构建 iOS 和 tvOS 应用。如果你需要维护旧项目或需要 UIKit 提供的某些特定功能,可能需要学习它。
  • AppKit: 用于构建 macOS 应用程序的 UI 框架。

你需要学习如何使用这些框架来创建视图、布局元素、处理用户交互、导航、管理数据等。

结语:持续学习与实践

Swift 是一门仍在不断发展的语言,Apple 每年都会带来新的特性和改进。学习 Swift 是一个持续的过程。

  • 多写代码: 理论学习固然重要,但只有通过不断编写代码、解决问题、尝试新特性,才能真正掌握它。利用 Playgrounds 进行实验,参与小型项目,逐步挑战更复杂的任务。
  • 阅读官方文档: Apple 的 Swift 官方文档 (The Swift Programming Language Guide) 是最权威、最全面的学习资源。
  • 参与社区: 加入 Swift 开发者论坛、Stack Overflow、关注 Swift相关的博客和 Twitter 账号,与其他开发者交流,学习他们的经验。
  • 贡献开源: 如果你对 Swift 本身或其生态系统感兴趣,可以参与到开源项目中。

掌握 Swift 将为你打开通往 Apple 庞大且充满活力的生态系统的大门,无论是开发下一款热门 App,还是构建强大的桌面工具,或是探索其他应用领域,Swift 都将是你手中强大的利器。祝你在这段学习旅程中收获满满,享受编程的乐趣!


THE END