本工具使用的開源套件

本工具程式碼中捆綁了 1 個開源套件。

Swift 速查 — 簡明參考

Swift 5.9 語法、值類型、協議與最常用標準庫速查手冊,覆蓋約 80% 日常場景。

Sw

Swift Swift 5.9

LLVM (swiftc / Swift toolchain) · 多範式(協議導向 · OO · 函數式) · 靜態 · 強類型

學習路徑

先學會 print 與變量綁定 → 掌握值類型(struct/enum)與可選值 Optional → 深入函數、閉包與集合 → 理解協議導向設計與擴展 → 用 do-catch 與 throws 處理錯誤 → 用 async/await 與 DispatchQueue 寫併發 → 再按需學 URLSession、日期與構建測試。FAQ 節適合回頭避坑。

1.Hello World 與構建環境

運行 Swift 程序、SwiftPM 項目與工具鏈。

最小程序

頂層代碼即程序入口。print 輸出。import 導入模塊。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 新建文件 main.swift:
import Foundation
print("Hello, world!")
// 運行:
// swift main.swift
// 編譯運行:
// swiftc main.swift -o app
// ./app
// 要點:
// 頂層語句直接執行
// 無需 main 函數(腳本模式)
// print 自動換行
// import Foundation 常用

運行與構建

swift 直接運行腳本。swiftc 編譯。swift run 運行包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 直接運行腳本:
// swift script.swift
// 編譯為可執行:
// swiftc main.swift -o app
// ./app
// 運行 SwiftPM 包:
// swift run
// 指定可執行目標:
// swift run MyTool
// 編譯優化:
// swiftc -O main.swift
// 編譯所有源文件:
// swiftc *.swift
// 調試信息:
// swiftc -g main.swift
// 檢查版本:
// swift --version

SwiftPM 項目

swift package init 初始化。Package.swift 清單。Sources 源碼目錄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 創建可執行包:
// swift package init --type executable
// 創建庫包:
// swift package init --type library
// 結構:
// Package.swift 清單
// Sources/ 源碼
// MyTool/ 目標目錄
// Tests/ 測試
// Package.swift:
// swift-tools-version:5.9
// import PackageDescription
// let package = Package(
// name: "MyTool",
// targets: [.executableTarget(
// name: "MyTool")]
// )
// 構建:swift build
// 運行:swift run
// 測試:swift test

import 導入

import 導入模塊。Foundation 常用。按需導入子模塊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 導入標準模塊:
import Foundation
import UIKit // iOS 界面
import SwiftUI // SwiftUI 界面
// Foundation 提供:
// 字符串、日期、文件、JSON
// 數值、集合的增強
// 子模塊:
import Foundation.NSURL
// 多平台條件導入:
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
// 導入自己模塊的庫:
// import MyLibrary
// import 語句在文件頂部

輸出與插值

print/print(items:)。字符串插值 \(值)。分隔符與終止符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let name = "Nick"
let age = 30
// 字符串插值:
print("\(name) 今年 \(age) 歲")
// 多值輸出:
print(1, 2, 3) // 空格分隔
print("a", "b", separator: "-") // a-b
print("x", terminator: "") // 不換行
// 調試輸出:
debugPrint(name)
// 格式控制:
let pi = 3.14159
print(String(format: "%.2f", pi)) // 3.14
// 數組/字典直接打印:
print([1, 2, 3])
print(["a": 1])
// 自定義類型實現 CustomStringConvertible

命令行參數

CommandLine.arguments 獲取參數。第一個是程序路徑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Foundation
// 全部參數:
let args = CommandLine.arguments
print("共 \(args.count) 個參數")
for (i, a) in args.enumerated() {
print("\(i): \(a)")
}
// 業務參數:
// args[0] 是程序路徑
// 從 args[1] 開始是參數
if args.count > 1 {
let name = args[1]
print("你好 \(name)")
}
// 簡單解析:
// 複雜參數用 ArgumentParser
import ArgumentParser // 需依賴
// 退出:
// exit(0) / exit(1)

多文件

同一包多個源文件共享類型。internal 訪問級別。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Sources/App/main.swift
let greeter = Greeter(name: "Swift")
greeter.greet()
// Sources/App/Greeter.swift
// 同包內類型自動可見:
struct Greeter {
let name: String
func greet() {
print("你好,\(name)!")
}
}
// 訪問級別:
// private 文件內
// internal 模塊內(默認)
// public 模塊外
// 編譯:
// swiftc main.swift Greeter.swift
// SwiftPM 自動包含 Sources/
// 文件名無關,類型名唯一即可

Xcode 集成

Xcode 項目組織。target、scheme。iOS 開發工作流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 項目結構:
// MyApp.xcodeproj
// Sources/
// Resources/
// Tests/
// target:構建單元
// iOS App / watchOS App
// Framework / Unit Test
// scheme:構建+運行組合
// 命令行構建:
// xcodebuild -scheme MyApp build
// 運行測試:
// xcodebuild test -scheme MyApp
// 模擬器:
// xcrun simctl list devices
// 導出 IPA:
// xcodebuild archive
// SwiftPM 包可直接拖入
// 與 SwiftPM 區別:
// Xcode 處理簽名與設備

2.變量與常量

var/let 綁定、類型推斷、作用域與命名。

var 與 let

let 常量不可變。var 變量可變。優先使用 let。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 常量:
let maxCount = 100
// maxCount = 200 // 錯誤:不可變
// 變量:
var score = 0
score = 10 // 允許修改
// 類型推斷:
let name = "Swift" // String
let number = 42 // Int
// 顯式類型:
let count: Int = 5
var price: Double = 3.5
// 命名原則:
// 能 let 就 let
// 需要修改才 var
// 併發環境不可變性更安全
// 集合用 var 可變、let 只讀

類型推斷

編譯器根據字面量與上下文推斷類型。複雜場景顯式標註。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 字面量推斷:
let a = 42 // Int
let b = 3.14 // Double
let c = "hi" // String
let d = true // Bool
// 上下文推斷:
var array: [Int] = []
array.append(1) // 元素推斷 Int
// 字面量可多類型:
let i: Int = 42
let f: Double = 42 // 允許
let u: UInt8 = 42
// 顯式標註場景:
// 泛型、可選值、複雜表達式
// 編譯器報錯時補充標註
// 類型別名:
typealias Age = Int
let age: Age = 30
// 快捷鍵 Option+Click 查看類型

顯式類型

冒號後標註類型。提高可讀性。轉換與字面量兼容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 基礎標註:
let name: String = "Swift"
let count: Int = 10
let ratio: Double = 0.5
// 可選值標註:
var optional: String? = nil
// 集合標註:
var list: [Int] = [1, 2]
var dict: [String: Int] = [:]
// 元組標註:
var pair: (Int, String) = (1, "a")
// 函數類型標註:
var handler: (Int) -> Void
// 何時需要:
// 空集合無法推斷
// 協議類型、泛型
// 公共 API 必須標註
// 有助於文檔與編譯期檢查

常量

let 聲明常量。引用類型常量不可變引用。編譯期常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 基本常量:
let days = 7
// 常量數組/字典:
let fixed = [1, 2, 3]
// fixed.append(4) // 錯誤:不可變
// 常量引用類型:
class Counter { var n = 0 }
let c = Counter()
// c 不可變,但對象屬性可變:
c.n += 1 // 允許
// 全局常量:
let appName = "GuruToolkit"
// 編譯期常量:
// static let / enum 命名空間
enum Config {
static let maxRetry = 3
}
print(Config.maxRetry)
// 常量可計算:
let total = days * 24

遮蔽

內層作用域同名變量遮蔽外層。if/switch 局部常量常見。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x = 10
// 內層遮蔽:
if true {
let x = 20 // 遮蔽外層
print(x) // 20
}
print(x) // 10
// 常見模式:
// 可選綁定同名:
var value: Int? = 5
if let value = value {
print(value) // 新常量 5
}
// 循環變量遮蔽:
for x in 1...3 {
print(x) // 1 2 3
}
// 注意:
// 遮蔽降低可讀性
// 複雜函數避免多層遮蔽
// 可選綁定利用遮蔽安全解包

作用域

{} 定義作用域。內層可訪問外層。外層不可訪問內層。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let outer = 1
// 塊作用域:
{
let inner = 2
print(outer) // 內層可訪問外層
print(inner)
}
// print(inner) // 錯誤:已離開作用域
// 函數作用域:
func demo() {
let local = "函數內"
print(local)
}
// 循環作用域:
for i in 1...3 {
let sq = i * i
print(sq)
}
// 同名規則:
// 同一作用域不能重複聲明
// 嵌套作用域可遮蔽
// 生命週期:
// 作用域結束變量銷燬
// 全局變量程序生命週期內存在

命名規範

Swift 命名約定。camelCase、清晰動詞、避免縮寫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 變量/常量:camelCase
let userName = "nick"
var totalCount = 0
// 類型:CamelCase
struct UserAccount {}
class NetworkManager {}
enum ColorChoice {}
// 協議:動詞/形容詞命名
protocol CanGreet {}
protocol Named {}
// 布爾屬性:is/has 前綴
var isEnabled = true
var hasPermission = false
// 方法:動詞開頭
func saveData() {}
func loadConfig() {}
// 官方 API 設計規範:
// 名稱讀作句子
// 避免縮寫與模稜兩可
// 文檔註釋 /// 説明用途

類型轉換

數值類型顯式轉換。init() 構造器。字符串與數值互轉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 數值轉換(顯式):
let i = 42
let d = Double(i) // 42.0
let u = UInt8(i) // 42
// 不自動隱式轉換:
// let x = 1 + 2.0 // 錯誤
let x = Double(1) + 2.0 // 正確
// 字符串轉數值:
let s = "42"
let n = Int(s) // Int? 可選
let f = Double("3.14") // Double?
// 數值轉字符串:
let str = String(42)
let str2 = "\(3.14)"
// 可選解包:
if let n = Int(s) {
print(n)
}
// 截斷:Int(3.9) // 3

3.類型系統

基礎類型、元組、結構體、枚舉與可選值。

基礎類型

Int、Double、Bool、String、Character。值類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 整數:
let i: Int = -10
let u: UInt = 10
// 浮點:
let f: Float = 1.5 // 32 位
let d: Double = 3.14159 // 64 位
// 布爾:
let t: Bool = true
let f2: Bool = false
// 字符串與字符:
let s: String = "hello"
let c: Character = "A"
// 類型別名:
let whole: Int = 42
// 整數範圍:
print(Int.min, Int.max)
// 標準庫類型:
// Int/Double 是值類型
// 賦值拷貝而非引用
// 方法調用:i.description

整數類型

Int8-Int64、UInt 系列。平台 Int。溢出處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 有符號:Int8 Int16 Int32 Int64
// 無符號:UInt8 UInt16 UInt32 UInt64
// 平台默認:Int(64 位)
let a: Int8 = -128
let b: UInt8 = 255
let c: Int64 = 9_223_372_036_854_775_807
// 下劃線分隔:
let big = 1_000_000
// 溢出:
// 默認溢出報錯(編譯期字面量)
// 運算溢出用溢出運算符:
var x: UInt8 = 250
x = x &+ 10 // 迴繞 4
// &- &* &/ 溢出運算
// 檢查溢出:
// x.addingReportingOverflow(10)
// 優先級明確時類型標註

浮點數

Float/Double 精度。CGFloat 界面座標。運算與特殊值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Double 64 位 / Float 32 位
let d = 3.141592653589793
let f: Float = 3.14
// 科學計數:
let e = 1.5e3 // 1500.0
// 字面量推斷為 Double
// 運算:
let sum = d + 1.0
let sqrt = d.squareRoot()
let pow = pow(2.0, 10.0)
// 特殊值:
let nan = Double.nan
let inf = Double.infinity
nan.isNaN // true
inf.isInfinite // true
// 比較:
// 浮點相等比較需容忍誤差
let a = 0.1 + 0.2 // 0.30000000000000004
// 格式化:
String(format: "%.2f", a)
// 取整:
Int(3.7) // 3
3.7.rounded() // 4.0

元組

多元複合值。命名元素。解構。輕量數據分組。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 基礎元組:
let pair = (1, "one")
print(pair.0) // 1
print(pair.1) // "one"
// 命名元素:
let user = (name: "Nick", age: 30)
print(user.name)
print(user.age)
// 解構:
let (code, message) = (200, "OK")
print(code)
// 忽略部分:
let (x, _) = (1, 2)
// 類型標註:
let point: (x: Double, y: Double) = (0, 0)
// 函數返回多值:
func div(a: Int, b: Int) -> (q: Int, r: Int) {
(a / b, a % b)
}
let result = div(a: 10, b: 3)
print(result.q, result.r)
// 字典遍歷解構元組

結構體

struct 值類型。自動成員構造器。屬性與方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 定義結構體:
struct Point {
var x: Double
var y: Double
// 方法:
func distance() -> Double {
(x * x + y * y).squareRoot()
}
// 可變方法:
mutating func moveBy(dx: Double) {
x += dx
}
}
// 自動構造器:
let p = Point(x: 1, y: 2)
print(p.distance())
// 值類型拷貝:
var p2 = p
p2.x = 100
print(p.x) // 1,p 不受影響
// 計算屬性見 OOP 節
// 首選 struct 而非 class

枚舉

enum 相關值分組。關聯值。原始值。窮盡匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 簡單枚舉:
enum Direction {
case north, south, east, west
}
// 關聯值:
enum Result2 {
case success(Int)
case failure(String)
}
// 原始值:
enum Color: String {
case red = "#FF0000"
case green = "#00FF00"
}
// 使用:
let dir = Direction.north
switch dir {
case .north: print("北")
case .south: print("南")
case .east: print("東")
case .west: print("西")
}
// 關聯值提取:
let r = Result2.success(200)
if case .success(let v) = r {
print(v)
}
// 原始值:
print(Color.red.rawValue)
// 方法可加在枚舉上

可選值

Optional 表示可能為空。T? 語法。nil 表示無值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 聲明可選值:
var name: String? = nil
var age: Int? = 30
// 賦值:
name = "Nick"
// 解包方式:
// 1. 強制解包(危險):
// print(name!)
// 2. 可選綁定:
if let n = name {
print(n)
}
// 3. 空合運算符:
let display = name ?? "未知"
// 4. 可選鏈:
// name?.count
// 判斷為空:
if name == nil { print("無名字") }
// 實際類型:
// Optional<String> 是枚舉
// 含 .some 和 .none

嵌套類型

類型內定義類型。命名空間組織。枚舉攜帶相關類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 嵌套類型:
struct Player {
// 枚舉嵌套在結構體內:
enum State {
case idle, running, dead
}
var state: State = .idle
// 類型別名嵌套:
typealias Health = Int
var health: Health = 100
}
// 使用:
let p = Player()
let s: Player.State = .running
print(s)
// 訪問嵌套類型:
// Player.State
// 嵌套分類組織:
enum HTTP {
enum Status {
static let ok = 200
static let notFound = 404
}
}
print(HTTP.Status.ok)
// 好處:
// 相關類型就近定義
// 減少頂層命名衝突
// 表達“屬於誰”的關係
// 深嵌套保持合理

4.值類型與引用

值語義、類引用、寫時複製與內存佈局。

值 vs 引用

struct 值語義拷貝。class 引用共享。語言核心區別。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 值類型:
struct Point { var x = 0 }
var a = Point()
a.x = 10
var b = a // 拷貝
b.x = 99
print(a.x) // 10,獨立
// 引用類型:
class Box { var value = 0 }
let c = Box()
c.value = 5
let d = c // 共享引用
d.value = 100
print(c.value) // 100,同一對象
// 選擇原則:
// 值類型默認優先
// 需要共享/繼承用 class
// 需要比較相等語義用值類型
// struct 在 Swift 中佔主導

class 語義

class 引用類型。身份唯一。可變性與線程共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 類定義:
class Counter {
var count = 0
}
// 引用共享:
let c1 = Counter()
let c2 = c1
c2.count += 1
print(c1.count) // 1,同一對象
// 恆等比較:
// === 比較引用相同
if c1 === c2 { print("同一實例") }
// == 比較值(需 Equatable)
// 引用類型的可變性:
// 常量引用可修改屬性
// 多線程共享需注意同步
// 類支持繼承(OOP 節)
// 類有 deinit 析構

寫時複製

數組等值類型內部共享存儲。修改時才真正拷貝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 數組:
var a = [1, 2, 3]
var b = a // 共享內部存儲
print(a) // [1, 2, 3]
// 修改時才拷貝:
b.append(4)
print(a) // [1, 2, 3] 不變
// 寫時複製優化:
// 賦值幾乎零成本
// 首次修改才分配拷貝
// 大數組多次賦值高效
// 自定義類型可優化:
// 用 isKnownUniquelyReferenced
// 檢查引用唯一性
// 字符串/字典同樣優化
// 值語義 + 高性能兼具

恆等與相等

=== 引用恆等。== 值相等(Equatable)。區別重要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 恆等 ===:
class Person { var name: String
init(name: String) { self.name = name }
}
let p1 = Person(name: "Nick")
let p2 = p1
let p3 = Person(name: "Nick")
// 恆等:
print(p1 === p2) // true 同一對象
print(p1 === p3) // false 不同對象
// 值相等(需 Equatable):
struct Point: Equatable {
var x: Int, y: Int
}
let q1 = Point(x: 1, y: 2)
let q2 = Point(x: 1, y: 2)
print(q1 == q2) // true 值相同
// 規則:
// 引用類型用 === 判斷同一
// 值類型用 == 判斷相等
// 自定義 Equatable 實現
// 哈希與相等一致(Hashable)

weak 與 unowned

weak 弱引用不增計數。unowned 無主引用。打破循環引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
var pet: Pet?
deinit { print("Person 釋放") }
}
class Pet {
// weak 不持有:
weak var owner: Person?
deinit { print("Pet 釋放") }
}
var nick: Person? = Person()
var dog: Pet? = Pet()
nick?.pet = dog
dog?.owner = nick
// 循環引用已打破:
nick = nil // 釋放
print("nick 已釋放")
// weak 必須 var、可選
// unowned:假定始終有值
// class Restaurant {
// unowned var chef: Chef
// }
// 訪問已釋放 unowned 崩潰
// 優先 weak

inout 參數

inout 參數引用傳入。函數內修改原值。& 調用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// inout 函數:
func swapValues(_ a: inout Int, _ b: inout Int) {
let tmp = a
a = b
b = tmp
}
// 調用:
var x = 1
var y = 2
swapValues(&x, &y)
print(x, y) // 2 1
// 修改數組元素:
func increment(_ n: inout Int) {
n += 1
}
var nums = [10, 20]
increment(&nums[0])
print(nums) // [11, 20]
// 限制:
// 不能傳字面量/常量
// 不能同時傳同一變量兩次
// 屬性需 var 可變
// 與引用類型參數的區別:
// inout 是值進出的通道

指針互操作

UnsafePointer 與 C 互操作。內存管理責任。危險區域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 獲取變量指針:
var number = 42
// withUnsafePointer 作用域:
withUnsafePointer(to: &number) { ptr in
print(ptr.pointee) // 42
}
// 可變指針:
withUnsafeMutablePointer(to: &number) { ptr in
ptr.pointee = 100
}
print(number) // 100
// C 函數互操作:
// 數組傳 C 指針:
var arr = [1, 2, 3]
arr.withUnsafeBufferPointer { buf in
// buf.baseAddress 給 C
}
// 注意:
// 指針生命週期必須手動保證
// 不要保存指針逃出作用域
// 多數場景避免使用
// 性能瓶頸才考慮

內存佈局

類型內存佈局。字節對齊。影響性能與互操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 查看類型大小:
print(MemoryLayout<Int>.size) // 8
print(MemoryLayout<Double>.size) // 8
print(MemoryLayout<Bool>.size) // 1
// 對齊:
print(MemoryLayout<Int>.alignment)
// 結構體佈局:
struct Small {
let a: Int8 // 1 字節
let b: Int64 // 8 字節
}
print(MemoryLayout<Small>.size) // 含填充
// 緊湊佈局:
// 字段順序影響填充
// 大字段在前更緊湊
// 優化:
// 避免大量小結構體
// 數組連續存儲
// 與 C 互操作關心佈局
// 很少需要手動優化

5.流程控制

if、switch、guard、循環與可選綁定。

if / else

if 條件執行。else if 多分支。條件無需括號。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let score = 85
// 基本 if:
if score >= 60 {
print("及格")
}
// else if:
if score >= 90 {
print("優秀")
} else if score >= 60 {
print("及格")
} else {
print("不及格")
}
// 條件必須 Bool:
// if score { } // 錯誤
// 多條件:
if score >= 60 && score < 90 {
print("良好")
}
// 一行 if:
if score > 80 { print("高分") }
// 三目運算符:
let label = score >= 60 ? "過" : "不過"

switch

switch 窮盡匹配。區間、元組、綁定。不需要 break。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let value = 5
// 值匹配:
switch value {
case 1, 2, 3:
print("小")
case 4...6:
print("中")
default:
print("大")
}
// 區間與 where:
let temp = 25
switch temp {
case 0...10: print("冷")
case 11...30 where temp >= 20: print("暖")
case 11...30: print("温")
default: print("熱")
}
// 枚舉窮盡:
enum Grade { case a, b, c }
let g = Grade.a
switch g {
case .a: print("優秀")
case .b: print("良好")
case .c: print("及格")
}
// 元組匹配:
// switch (x, y) { case (0, 0): ... }

guard

guard 提前退出。else 必須退出作用域。else 有 else 分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// guard 提前返回:
func validate(_ age: Int?) {
guard let age = age else {
print("年齡為空")
return
}
// 這裏 age 已解包
guard age >= 18 else {
print("未成年")
return
}
print("成人,年齡 \(age)")
}
validate(nil)
validate(15)
validate(20)
// 與 if let 區別:
// guard 的解包值後續可用
// if let 只在分支內可用
// guard 的 else 必須退出:
// return/throw/break/continue
// 結構更清晰、縮進更淺
// 主流程邏輯保持在頂層

for-in 循環

for-in 遍歷範圍、數組、字典。帶索引遍歷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 範圍:
for i in 1...5 { // 含 5
print(i)
}
for i in 1..<5 { // 不含 5
print(i)
}
// 數組:
let names = ["a", "b", "c"]
for name in names {
print(name)
}
// 帶索引:
for (i, name) in names.enumerated() {
print("\(i): \(name)")
}
// 字典:
let scores = ["a": 90, "b": 80]
for (key, value) in scores {
print("\(key): \(value)")
}
// 倒序:
for i in (1...3).reversed() {}
// 步長:
for i in stride(from: 0, to: 10, by: 2) {}
// 索引數組:
for i in names.indices { print(names[i]) }

while 循環

while 條件循環。repeat-while 先執行後判斷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var n = 3
// while:
while n > 0 {
print(n)
n -= 1
}
// repeat-while(至少執行一次):
var count = 0
repeat {
count += 1
print("第 \(count) 次")
} while count < 3
// 退出循環:
var found = false
var i = 0
while !found && i < 10 {
i += 1
if i == 5 { found = true }
}
// 用途:
// 條件未知的循環
// 輪詢、重試邏輯
// 與 for-in 區別:
// 不需要已知次數時用 while
// repeat-while 適合菜單交互

break 與 continue

break 退出循環。continue 跳過本輪。帶標籤退出嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// continue:
for i in 1...10 {
if i % 2 == 0 {
continue // 跳過偶數
}
print(i) // 1 3 5 7 9
}
// break:
for i in 1...10 {
if i > 5 {
break // 提前退出
}
print(i)
}
// 標籤退出嵌套:
outer: for i in 1...3 {
for j in 1...3 {
if i * j == 6 {
print("命中 \(i) \(j)")
break outer // 退出全部
}
}
}
// 標籤命名任意
// 用於複雜嵌套控制流
// 適度使用,過度降低可讀性

if let

可選綁定解包。guard let 提前。多值同時解包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var name: String? = "Nick"
var age: Int? = 30
// 單個解包:
if let name = name {
print("名字 \(name)")
}
// 多個解包(逗號):
if let name = name, let age = age {
print("\(name) \(age) 歲")
}
// 帶條件:
if let name = name, name.count > 2 {
print(name)
}
// while 中解包:
var nums = [1, 2, 3]
while let n = nums.popLast() {
print(n) // 3 2 1
}
// 不關心值只判空:
if name != nil { print("有值") }
// 注意:
// if let 分支外不可用
// 需要後續使用用 guard let

模式匹配

switch/if 中的模式。case let、where。類型匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 關聯值綁定:
enum Result3 {
case success(Int)
case failure(String)
}
let r = Result3.success(200)
switch r {
case .success(let code):
print("成功 \(code)")
case .failure(let msg):
print("失敗 \(msg)")
}
// 區間匹配:
let grade = 85
switch grade {
case 90...100: print("A")
case 80..<90: print("B")
case 70..<80: print("C")
default: print("D")
}
// 類型匹配:
let any: Any = 42
switch any {
case let i as Int: print("整數 \(i)")
case let s as String: print("字符串 \(s)")
default: print("其他")
}
// where 條件:
switch grade {
case let g where g % 5 == 0: print("整五分")
default: print("其他分")
}

6.函數

函數定義、參數標籤、返回值、函數類型與閉包。

函數定義

func 定義函數。參數與返回值。調用語法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基本函數:
func greet(name: String) -> String {
"你好,\(name)!"
}
// 無返回值:
func sayHello() {
print("hello")
}
// 無返回也可標 Void:
func log(_ msg: String) -> Void {}
// 調用:
let g = greet(name: "Nick")
print(g)
sayHello()
// 多參數:
func add(a: Int, b: Int) -> Int {
a + b
}
// 參數默認標籤=參數名
// 調用需寫標籤
// 最後一個表達式是返回值

參數標籤

參數標籤與內部名。_ 省略標籤。可讀性設計。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 自定義標籤:
func move(from start: Int, to end: Int) {
print("從 \(start)\(end)")
}
move(from: 1, to: 10)
// 省略標籤:
func sum(_ a: Int, _ b: Int) -> Int {
a + b
}
print(sum(1, 2))
// 混合:
func configure(_ name: String, debug: Bool = false) {
print(name, debug)
}
configure("app") // 省略標籤
configure("app", debug: true)
// API 設計規範:
// 標籤使調用讀作句子
// 介詞 with/for/in 常見
// 標籤用詞清晰避免歧義

默認參數值

參數默認值。調用時可省略。必須尾部或都有默認。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 默認值:
func greet2(_ name: String, times: Int = 1) {
for _ in 1...times {
print("你好 \(name)")
}
}
greet2("Nick") // 用默認 1
greet2("Nick", times: 3)
// 默認值常見用法:
func fetch(
url: String,
timeout: Double = 30,
retries: Int = 3
) {
print(url, timeout, retries)
}
// 調用任意組合:
fetch(url: "https://x")
fetch(url: "https://x", retries: 5)
// 規則:
// 默認值在參數列表最後
// 默認參數可不傳
// 默認值在函數簽名中體現
// 可讀性與靈活性的平衡

返回值

返回值類型。多值用元組。隱式返回單表達式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 單一返回值:
func square(_ n: Int) -> Int {
n * n
}
// 元組多值返回:
func stats(_ nums: [Int]) -> (min: Int, max: Int, sum: Int) {
(nums.min() ?? 0, nums.max() ?? 0, nums.reduce(0, +))
}
let s = stats([3, 1, 4, 1])
print(s.min, s.max, s.sum)
// 可選值返回:
func findIndex(of value: Int, in arr: [Int]) -> Int? {
arr.firstIndex(of: value)
}
if let i = findIndex(of: 4, in: [1, 2, 4]) {
print(i) // 2
}
// 無返回值:
func noReturn() {}
// 隱式返回:
// 單表達式省略 return
// 多語句需顯式 return

可變參數

... 收集多個參數為數組。任意數量。最後一個參數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 可變參數:
func average(_ numbers: Double...) -> Double {
let sum = numbers.reduce(0, +)
return numbers.isEmpty ? 0 : sum / Double(numbers.count)
}
print(average(1, 2, 3, 4)) // 2.5
print(average()) // 0
// 可變參數作為數組使用
// 打印任意數量:
func logAll(_ items: String...) {
for item in items {
print(item)
}
}
logAll("a", "b", "c")
// 規則:
// 可變參數放最後
// 一個函數最多一個
// 內部是 [T] 數組
// 調用時逗號分隔

inout 參數

inout 修改外部變量。& 傳址。值類型原地修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 交換:
func swapTwo<T>(_ a: inout T, _ b: inout T) {
let tmp = a
a = b
b = tmp
}
var x = 5
var y = 10
swapTwo(&x, &y)
print(x, y) // 10 5
// 值類型修改:
func scale(_ v: inout Double, by factor: Double) {
v *= factor
}
var price = 100.0
scale(&price, by: 0.8)
print(price) // 80.0
// 規則:
// 傳變量而非常量/字面量
// & 前綴
// 與引用類型區別:
// inout 是函數內外的值往返
// 不創建新實例
// 適合大值類型避免拷貝

函數類型

函數可作為類型。賦值、傳參、返回。一等公民。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 函數類型標註:
func add(a: Int, b: Int) -> Int { a + b }
// (Int, Int) -> Int
// 賦值:
let operation: (Int, Int) -> Int = add
print(operation(1, 2))
// 作為參數:
func apply(_ f: (Int) -> Int, to value: Int) -> Int {
f(value)
}
func double(_ n: Int) -> Int { n * 2 }
print(apply(double, to: 5)) // 10
// 返回函數:
func makeAdder(_ base: Int) -> (Int) -> Int {
{ n in base + n }
}
let addTen = makeAdder(10)
print(addTen(5)) // 15
// 可選函數類型:
var handler: (() -> Void)?
// 調用需處理可選
// 類型匹配嚴格(含標籤)

閉包

閉包捕獲上下文。尾隨閉包。簡寫參數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 定義閉包:
let square2 = { (n: Int) -> Int in
n * n
}
print(square2(4))
// 捕獲變量:
var counter = 0
let increment = {
counter += 1
}
increment()
increment()
print(counter) // 2
// 尾隨閉包:
func perform(_ action: () -> Void) {
action()
}
perform {
print("尾隨閉包")
}
// 排序簡寫:
let nums = [3, 1, 2]
let sorted = nums.sorted { $0 < $1 }
print(sorted)
// 簡寫:
// $0 $1 參數簡寫
// 捕獲列表 [weak self]
// @escaping 逃逸閉包

7.字符串

String 操作、插值、子串、Unicode 與格式化。

String 基礎

String 值類型。字面量與可變性。Character 組成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 創建字符串:
let greeting = "Hello"
// 多行字符串:三引號字面量
// let multi = """
// 多行
// 字符串
// """
// 等價(\n 換行):
let multi = "多行\n字符串"
// 可變字符串:
var text = "start"
text += " end"
// 空字符串:
let empty = ""
let empty2 = String()
// 字符序列:
// String 由 Character 組成
// 遍歷字符:
for c in text {
print(c)
}
// 計數(字符數):
print(text.count)
// 判斷空:
text.isEmpty
// 字符串是值類型:
// 賦值拷貝
// 高效(寫時複製)

插值

\(表達式) 嵌入值。類型安全。任意表達式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let name = "Nick"
let age = 30
let score = 95.5
// 基礎插值:
let msg = "\(name) 今年 \(age) 歲"
print(msg)
// 表達式插值:
let calc = "\(age * 2)\(age) 的兩倍"
// 調用方法:
let upper = "大寫: \(name.uppercased())"
// 格式化值:
let formatted = "分數: \(String(format: "%.1f", score))"
// 嵌套插值:
let nested = "\(name)\("\(age)".count) 個字符"
// 轉義反斜槓:
let backslash = "反斜槓:\\"
// 注意:
// 插值類型需可顯示
// 自定義類型實現 CustomStringConvertible

拼接

字符串拼接。append。join。+= 操作符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// + 拼接:
let a = "hello"
let b = " world"
let c = a + b // 新字符串
// += 追加:
var s = "start"
s += " - more"
// append:
s.append("!")
// 字符追加:
s.append(Character("?"))
// 數組 join:
let parts = ["a", "b", "c"]
let joined = parts.joined(separator: ", ")
print(joined) // "a, b, c"
// 性能:
// 小字符串拼接開銷小
// 大量拼接:
// 用數組 + joined
// 或可變字符串一次構建
// 字符串是值類型,拼接創建新值

多行字符串

三引號多行。縮進裁剪。內插值。 分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 多行字符串用三引號字面量:
// let poem = """
// 牀前明月光,
// 疑是地上霜。
// """
// 等價實現(\n 換行):
let poem = "牀前明月光,\n疑是地上霜。"
print(poem)
// 縮進規則:以閉合引號縮進為基準
// 包含引號:
// let quote = """
// He said "hi"
// """
// 多行插值:
let name = "Rust"
// let info = """
// 語言:\(name)
// 類型:靜態
// """
// 反斜槓取消換行:
// let oneLine = """
// 一行內容 \
// 繼續
// """

常用方法

大小寫、查找、替換、分割、修剪。常用字符串 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let s = " Hello, Swift "
// 大小寫:
let lower = s.lowercased()
let upper = s.uppercased()
// 修剪:
let trimmed = s.trimmingCharacters(
in: .whitespacesAndNewlines)
// 判斷:
s.contains("Swift")
s.hasPrefix(" ")
s.hasSuffix(" ")
// 查找:
s.firstIndex(of: ",")
// 替換:
let replaced = s.replacingOccurrences(
of: "Swift", with: "Go")
// 分割:
let parts = "a,b,c".split(separator: ",")
// 前綴/後綴:
let pre = s.prefix(5)
// 判斷空:s.isEmpty
// 重複:
String(repeating: "ab", count: 3)

子串

Substring 引用原串。切片返回 Substring。轉 String 持有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let full = "Hello, Swift World"
// 索引範圍切片:
let start = full.index(full.startIndex, offsetBy: 7)
let end = full.index(start, offsetBy: 5)
let sub = full[start..<end] // "Swift"
print(sub)
// 注意:Substring 類型
// 與原字符串共享存儲
// 轉 String 獨立持有:
let owned = String(sub)
// 前綴後綴是 Substring:
let p = full.prefix(5) // "Hello"
// 用 Range 切:
if let range = full.range(of: "Swift") {
print(full[range])
}
// 多字節安全:
// 索引按字符邊界
// 不要用整數下標
// 頻繁用需轉 String

Unicode

字符由 Unicode 標量組成。碼點。擴展字位簇。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Unicode 字面量:
let heart = "\u{2764}"
print(heart) // ❤
// 擴展字位簇:
let e = "\u{E9}" // é
let combo = "e\u{301}" // é(組合)
print(e == combo) // true,同字位簇
// 字符(字位簇)計數:
let flag = "🇨🇳"
print(flag.count) // 1(區域指示符組合)
// 遍歷字位簇:
for c in "你好👍" {
print(c)
}
// 碼點:
for scalar in "中".unicodeScalars {
print(scalar.value) // 20013
}
// 規範化:
// .precomposedStringWithCanonicalMapping()
// 排序比較需規範化

格式化

String(format:) C 風格格式。數字填充與精度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 基礎格式:
let pi = 3.14159
let f = String(format: "%.2f", pi)
print(f) // "3.14"
// 整數補零:
String(format: "%05d", 42) // "00042"
// 寬度對齊:
String(format: "%10s", "hi") // 右對齊
// 科學計數:
String(format: "%e", 1234.0)
// 十六進制:
String(format: "%x", 255) // "ff"
// 多佔位:
String(format: "%d-%@", 2026, "08-02")
// 注意:
// %@ 對象、%d 整數、%f 浮點
// %lld 64 位整數
// 更 Swift 的方式:
// 優先用插值與描述
// 需要精確格式才用 format

8.集合

Array、Dictionary、Set、高階函數與區間。

Array

有序值集合。泛型 [T]。增刪改查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 創建:
var nums = [1, 2, 3]
let empty: [Int] = []
let filled = [Int](repeating: 0, count: 5)
// 字面量類型推斷:
let names = ["a", "b"]
// 訪問:
print(nums[0])
print(nums.first ?? 0)
print(nums.last ?? 0)
// 增刪:
nums.append(4)
nums.insert(0, at: 0)
nums.removeLast()
nums.remove(at: 1)
// 遍歷:
for n in nums { print(n) }
// 其他:
nums.count
nums.isEmpty
nums.contains(2)
// 求和:
let sum = nums.reduce(0, +)
// 排序:
nums.sorted()

數組操作

切片、替換、合併、查找。常用高階方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var arr = [1, 2, 3, 4, 5]
// 切片:
let slice = arr[1...3] // ArraySlice
print(slice)
// 範圍替換:
arr.replaceSubrange(0..<2, with: [9, 9])
// 合併:
let combined = arr + [10]
// 查找:
arr.first { $0 > 3 }
arr.last { $0 > 3 }
arr.contains(5)
arr.firstIndex(of: 3)
// 批量轉換:
let doubled = arr.map { $0 * 2 }
let evens = arr.filter { $0 % 2 == 0 }
// 分區:
let (small, big) = arr.partitioned {
$0 < 3
}
// 前綴後綴:
arr.prefix(2)
arr.suffix(2)
// 交換:
arr.swapAt(0, 1)

Dictionary

鍵值對集合。[String: T]。無序。O(1) 查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 創建:
var scores: [String: Int] = [:]
var config = ["env": "dev", "port": "8080"]
// 增改:
scores["alice"] = 90
scores["bob"] = 85
scores["alice"] = 95 // 覆蓋
// 讀取(可選):
let alice = scores["alice"] // Int?
let missing = scores["x"] // nil
// 默認值:
let v = scores["x"] ?? 0
// 刪除:
scores["bob"] = nil
scores.removeValue(forKey: "alice")
// 遍歷:
for (k, v) in scores {
print("\(k): \(v)")
}
// 鍵列表/值列表:
Array(scores.keys)
Array(scores.values)
// 合併:
config.merge(["port": "9090"]) { _, new in new }
// 計數與空:
scores.count

Set

無序唯一元素集合。哈希集合。集合運算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 創建:
var set: Set<Int> = [1, 2, 3]
let other: Set = [3, 4, 5]
// 增刪查:
set.insert(4)
set.remove(1)
set.contains(2)
// 集合運算:
let union = set.union(other) // 並
let intersect = set.intersection(other) // 交
let diff = set.subtracting(other) // 差
let sym = set.symmetricDifference(other) // 對稱差
// 判斷關係:
set.isSubset(of: other)
set.isSuperset(of: other)
set.isDisjoint(with: other)
// 遍歷:
for v in set { print(v) }
// 轉數組:
let arr = Array(set)
// 用途:
// 去重、存在性檢查
// 去重:
let dup = Array(Set([1, 1, 2, 3]))
print(dup)

遍歷

數組/字典/集合遍歷。帶索引。倒序。過濾條件遍歷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
let items = ["a", "b", "c"]
// 基礎遍歷:
for item in items {
print(item)
}
// 帶索引:
for (i, item) in items.enumerated() {
print("\(i): \(item)")
}
// 下標遍歷:
for i in items.indices {
print(items[i])
}
// 倒序:
for item in items.reversed() {}
// 字典遍歷:
let dict = ["a": 1, "b": 2]
for (k, v) in dict {
print("\(k): \(v)")
}
// 條件過濾遍歷:
for n in 1...10 where n % 2 == 0 {
print(n) // 2 4 6 8 10
}
// 跳過與退出:
// continue / break
// forEach:
items.forEach { print($0) }

高階函數

map、filter、reduce、compactMap、sorted。函數式處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let nums = [1, 2, 3, 4, 5]
// map 映射:
let doubled = nums.map { $0 * 2 }
// filter 過濾:
let evens = nums.filter { $0 % 2 == 0 }
// reduce 歸約:
let sum = nums.reduce(0, +)
// compactMap 去 nil:
let strs = ["1", "a", "3"]
let ints = strs.compactMap { Int($0) }
print(ints) // [1, 3]
// flatMap 展平:
let grid = [[1, 2], [3]]
let flat = grid.flatMap { $0 }
// sorted 排序:
let sorted = nums.sorted { $0 > $1 }
// 鏈式組合:
let result = nums
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
.reduce(0, +)
print(result) // 4 + 16 = 20
// forEach 遍歷
// 惰性:lazy.map

嵌套集合

集合的組合。多維數組、字典內數組、結構體集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 二維數組:
var grid = [[Int]]()
grid.append([1, 2, 3])
grid.append([4, 5, 6])
print(grid[0][1]) // 2
// 重複初始化:
var matrix = [[Int]](
repeating: [Int](repeating: 0, count: 3), count: 3)
matrix[1][2] = 9
// 字典內數組:
var groups: [String: [String]] = [:]
groups["dev"].append("nick") // 錯誤
// 需先初始化:
groups["dev"] = ["nick"]
groups["dev", default: []].append("tom")
// 結構體集合:
struct Task { var title: String; var done = false }
var tasks: [Task] = [Task(title: "a")]
tasks[0].done = true
// 數組字典:
let scores: [[String: Int]] = [
["math": 90], ["eng": 85]
]
print(scores[0]["math"] ?? 0)

Range

半開區間 ..<、閉區間 ...。數組切片與循環。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 範圍類型:
let closed = 1...5 // 含 5
let halfOpen = 1..<5 // 不含 5
// 循環:
for i in 1...3 {
print(i)
}
// 數組切片:
let arr = [10, 20, 30, 40]
let sub = arr[1...2] // [20, 30]
// 單側範圍:
let head = arr[..<2] // [10, 20]
let tail = arr[2...] // [30, 40]
// 檢查包含:
(1...5).contains(3) // true
// 步長:
for i in stride(from: 0, to: 10, by: 2) {
print(i) // 0 2 4 6 8
}
// 反轉:
for i in (1...3).reversed() {}
// 邊界:
// 空範圍:1..<1
// 浮點範圍(限制多)
// Range 用於下標與模式匹配

9.內存管理

ARC 引用計數、弱引用、循環引用與內存優化。

ARC

自動引用計數管理類實例。引用計數歸零釋放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Device {
var name = ""
deinit {
print("\(name) 釋放")
}
}
// 引用計數:
var d1: Device? = Device() // count 1
d1?.name = "phone"
var d2 = d1 // count 2
var d3 = d1 // count 3
d1 = nil // count 2
d2 = nil // count 1
d3 = nil // count 0,釋放
// 規則:
// 每強引用 +1
// 計數歸零自動釋放
// deinit 可觀察釋放
// 值類型(struct)不參與 ARC
// 無需手動管理
// 弱引用不計數

weak/unowned

弱引用不增計數。防止循環引用。訪問已釋放處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Parent {
var child: Child?
}
class Child {
// weak 不持有父:
weak var parent: Parent?
}
var parent: Parent? = Parent()
var child: Child? = Child()
parent?.child = child
child?.parent = parent
// 循環已打破:
parent = nil
// child 的 parent 自動變 nil
// weak 特性:
// 必須 var、必須可選
// 對象釋放自動變 nil
// unowned:
// class CreditCard {
// unowned let owner: Person
// }
// unowned 假定一直存在
// 訪問已釋放 unowned 崩潰
// 優先級:weak > unowned

自動釋放池

autoreleasepool 延遲釋放。大循環內存控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 大批量循環:
var results: [String] = []
// 循環外包池:
for i in 0..<1000 {
autoreleasepool {
// 臨時對象在此釋放:
let temp = "臨時數據 \(i)"
results.append(temp)
// 其他臨時對象...
}
}
// 每輪迭代結束釋放
// 用途:
// 大循環峯值內存
// 處理大量圖片/文件
// 耗時任務臨時對象
// 注意:
// 現代 Swift 多自動管理
// 需要手動時才用
// 池內 return 提前釋放
// ARC 下多數情況無需

拷貝語義

值類型拷貝安全。大類型優化。共享與唯一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 值類型拷貝:
var arr1 = [1, 2, 3]
var arr2 = arr1 // 寫時複製
// 修改觸發真拷貝:
arr2.append(4)
print(arr1) // [1, 2, 3] 不變
// 大字符串/數組/字典:
// 賦值廉價(共享存儲)
// 修改才分配
// 自定義 struct 同樣安全:
struct Config {
var theme = "dark"
var font = 14
}
var c1 = Config()
var c2 = c1
c2.theme = "light"
print(c1.theme) // dark
// 拷貝原則:
// 值語義防意外共享
// 線程安全(無共享狀態)
// 需要共享用 class

循環引用

兩個類互相強引用形成環。內存泄漏。weak 打破。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Owner {
var pet: Pet?
deinit { print("Owner 釋放") }
}
class Pet {
// BAD:強引用父,形成環
// var owner: Owner?
// GOOD:weak 弱引用
weak var owner: Owner?
deinit { print("Pet 釋放") }
}
var owner: Owner? = Owner()
var pet: Pet? = Pet()
owner?.pet = pet
pet?.owner = owner
// 無環時正常釋放:
owner = nil
pet = nil
// 診斷:
// Instruments 泄漏檢查
// deinit 未調用 = 泄漏
// 其他循環場景:
// 閉包捕獲 self 強引用
// 用 [weak self] 打破
// Timer/Delegate 注意

棧與堆

值類型棧上、引用類型堆上。分配與性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 棧上分配:
struct Point { var x = 0 }
let p = Point() // 棧上(多數情況)
// 堆上分配:
class Box { var v = 0 }
let b = Box() // 堆上
// 大結構體優化:
// 超過一定大小也堆上
// 數組元素內聯存儲
// 性能影響:
// 棧分配快、自動釋放
// 堆分配需 ARC 管理
// 大量小對象
// 用 struct 優於 class
// 結構體數組連續內存:
struct Item { var id = 0 }
let items = [Item(), Item()] // 連續
// 參考:
// 值類型默認棧
// 逃逸才上堆
// 無需過度關注

lazy 存儲

lazy 延遲初始化。用到才創建。耗時屬性優化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// lazy 屬性:
class DataLoader {
// 用到才加載:
lazy var config: [String: String] = {
print("加載配置")
return ["env": "dev"]
}()
}
let loader = DataLoader()
// 未訪問不執行:
print("已創建")
// 首次訪問觸發:
print(loader.config)
// 後續直接返回:
print(loader.config)
// 特點:
// 只初始化一次
// 線程不安全(併發需注意)
// 閉包內可用 self
// 適合:
// 昂貴構建
// 可能不使用的屬性
// 依賴其他屬性的初始化
// 全局變量也 lazy

內存優化

減少堆分配、複用緩衝、避免大拷貝。性能調優。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 減少臨時分配:
// 複用可變字符串:
var buffer = ""
for i in 0..<1000 {
// 追加而非拼接:
buffer.append("\(i),")
}
// 預分配容量:
var nums: [Int] = []
nums.reserveCapacity(1000)
for i in 0..<1000 {
nums.append(i)
}
// 避免不必要拷貝:
// 傳 inout 修改大數組
// 用 ArraySlice 共享
// 值類型注意
// 大結構體傳引用
// 用 withUnsafeBytes
// 檢查工具:
// Instruments Allocations
// 時間分析優先
// 別過早優化

10.類與協議

class、繼承、協議、擴展與面向協議設計。

class 定義

類定義屬性與方法。引用類型。初始化器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Animal {
var name: String
// 初始化器:
init(name: String) {
self.name = name
}
// 方法:
func speak() {
print("\(name) 叫了一聲")
}
}
// 實例化:
let dog = Animal(name: "旺財")
dog.speak()
// 引用語義:
let same = dog
same.name = "小黑"
print(dog.name) // 小黑
// 屬性默認值:
class Counter {
var count = 0
init() {}
}
// deinit 析構:
deinit {
print("實例釋放")
}
// 類可繼承、可擴展
// 與 struct 對比
// 類需要 init 保證所有存儲屬性

繼承

子類繼承父類。存儲屬性、方法。最終類 final。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Vehicle {
var speed = 0
func describe() -> String {
"速度 \(speed)"
}
}
// 子類:
class Car: Vehicle {
var wheels = 4
override func describe() -> String {
"汽車 \(wheels) 輪,\(super.describe())"
}
}
let car = Car()
car.speed = 100
print(car.describe())
// 繼承規則:
// 單繼承
// override 覆蓋必須標註
// 訪問父類用 super
// 不能繼承的:
// final class 不可繼承
// 私有屬性不可見
// 指定/便捷初始化器
// 優先組合與協議

方法重寫

override 覆蓋方法、屬性、初始化器。super 調用父類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Base {
var label = "base"
func greet() {
print("Base: \(label)")
}
// 可重寫屬性:
var value: Int {
get { 10 }
}
}
class Sub: Base {
// 重寫方法:
override func greet() {
super.greet() // 先調父類
print("Sub 額外處理")
}
// 重寫屬性:
override var value: Int {
get { super.value + 5 }
}
}
let s = Sub()
s.greet()
print(s.value)
// 規則:
// override 必寫
// 可讀性:
// final 阻止重寫
// final func 防再覆寫
// 屬性重寫用 get/set/willSet/didSet
// 靜態方法可重寫

協議

protocol 定義要求。類型遵守。面向協議設計核心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定義協議:
protocol Greetable {
var name: String { get }
func greet() -> String
}
// 遵守協議:
struct Person: Greetable {
var name: String
func greet() -> String {
"你好,\(name)!"
}
}
struct Robot: Greetable {
var name: String
func greet() -> String {
"嗶,\(name) 就緒"
}
}
// 協議類型使用:
let things: [Greetable] = [
Person(name: "Nick"),
Robot(name: "R2"),
]
for t in things {
print(t.greet())
}
// 協議可繼承:
protocol NamedGreetable: Greetable {
var age: Int { get }
}
// 協議默認實現用擴展

協議擴展

extension 提供默認實現。約束擴展。協議默認方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 協議:
protocol Describable {
var description: String { get }
}
// 默認實現:
extension Describable {
var description: String {
"描述:" + Self.self.description
}
}
// 約束擴展:
extension Describable where Self: Equatable {
func isSameAs(_ other: Self) -> Bool {
self == other
}
}
// 為類型添加協議:
struct Cat: Describable {
var name: String
}
// 使用默認實現:
let cat = Cat(name: "喵")
print(cat.description)
// 協議 + 擴展:
// 默認方法減少樣板
// 協議繼承組合
// where 約束細化
// 面向協議編程的基礎

擴展

extension 給現有類型加方法。分類組織代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 擴展內置類型:
extension String {
// 加方法:
func isEmail() -> Bool {
self.contains("@")
}
// 計算屬性:
var wordCount: Int {
self.split(separator: " ").count
}
}
print("[email protected]".isEmail())
print("hi there".wordCount)
// 擴展自己的類型:
struct Point { var x: Double }
extension Point {
func doubled() -> Point {
Point(x: x * 2)
}
}
// 遵守協議用擴展:
extension Point: Equatable {}
// 分組組織:
// 一個類型多個 extension
// 按功能分類
// 不能加存儲屬性
// 不能改既有實現

屬性觀察器

willSet/didSet 監聽屬性變化。界面更新、校驗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var user: String = "" {
willSet {
print("即將改為 \(newValue)")
}
didSet {
print("已從 \(oldValue) 改為 \(user)")
}
}
user = "Nick"
user = "Tom"
// 輸出觀察日誌
// 應用場景:
// 界面綁定刷新
// 數據校驗
// 記錄變更
// 規則:
// 初始化不觸發
// 類與 struct 都可
// 存儲屬性才能觀察
// 計算屬性用 get/set
// willSet/didSet 參數可命名
// 併發訪問注意線程

計算屬性

get/set 計算值。不存儲。派生數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct Circle {
var radius: Double
// 計算屬性:
var area: Double {
// 只讀:
Double.pi * radius * radius
}
// 讀寫:
var diameter: Double {
get {
radius * 2
}
set {
radius = newValue / 2
}
}
}
var c = Circle(radius: 5)
print(c.area) // 78.54
print(c.diameter) // 10
c.diameter = 20
print(c.radius) // 10
// 特點:
// 每次訪問重新計算
// 不佔存儲
// 派生值始終一致
// get 必須有
// set 用 newValue
// 也可自定義參數名
// 與存儲屬性對比

11.錯誤處理

throws、do-catch、try、defer 與自定義錯誤。

Error 協議

Error 協議標記錯誤類型。枚舉定義錯誤分類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 錯誤枚舉:
enum FileError: Error {
case notFound
case noPermission
case invalidData(String)
}
// 簡單錯誤:
struct SimpleError: Error {
let message: String
}
// 分類錯誤:
enum LoginError: Error {
case emptyUsername
case wrongPassword
case tooManyAttempts
}
// 用途:
// 標記可拋錯誤
// 攜帶信息:
// case invalidData(String)
// 錯誤符合可比較:
// 可加 Equatable 派生
// LocalizedError 提供描述
// 錯誤設計清晰分類

throws

throws 標記可拋函數。throw 拋出錯誤。調用需處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 拋錯函數:
func divide(_ a: Int, by b: Int) throws -> Int {
guard b != 0 else {
throw DivideError.zeroDivision
}
return a / b
}
enum DivideError: Error {
case zeroDivision
}
// 調用必須處理:
// 1. try! 危險
// let x = try! divide(1, by: 0)
// 2. try? 轉可選
let x = try? divide(1, by: 0)
print(x) // nil
// 3. try + do-catch
// 4. 傳播給調用者
func safeDivide() throws -> Int {
try divide(10, by: 2)
}
// 鏈式 throw:
// 函數標記 throws 即可透傳

do-catch

do 塊中 try。catch 捕獲錯誤。多分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum CalcError: Error {
case zeroDiv
case overflow
}
func compute() throws -> Int {
throw CalcError.zeroDiv
}
// 捕獲:
do {
let result = try compute()
print(result)
} catch CalcError.zeroDiv {
print("除以零")
} catch CalcError.overflow {
print("溢出")
} catch {
print("其他錯誤: \(error)")
}
// 綁定錯誤:
// catch let e as CalcError
// 一個 do 多個 try
// catch 從具體到一般
// error 隱式變量
// 未捕獲:
// 傳播到調用者
// 沒有 catch 需 throws

try 變體

try 需要 catch。try? 轉可選。try! 強制(崩潰風險)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func risky() throws -> Int { 42 }
// try:必須 do-catch 或傳播
// 語法要求:
func caller() throws {
let v = try risky()
print(v)
}
// try?:失敗轉 nil
let a = try? risky() // Int?
print(a ?? 0)
// try!:失敗崩潰
// 適合確定不失敗:
let b = try! risky()
print(b)
// 例子:
// let data = try? Data(contentsOf: url)
// 強制解包的權衡:
// try! 失敗直接崩潰
// 生產謹慎使用
// 測試/確定場景可用
// try? 適合“失敗也可”場景

自定義錯誤

錯誤攜帶上下文。LocalizedError 描述。錯誤碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 帶上下文:
enum NetworkError: LocalizedError {
case timeout(Int)
case serverError(code: Int, message: String)
// 本地化描述:
var errorDescription: String? {
switch self {
case .timeout(let s):
return "超時 \(s) 秒"
case .serverError(let code, let msg):
return "服務器 \(code): \(msg)"
}
}
}
// 使用:
func fetch() throws {
throw NetworkError.serverError(code: 500, message: "內部錯誤")
}
do {
try fetch()
} catch let e as NetworkError {
print(e.errorDescription ?? e)
}
// 捕獲任意 Error:
// catch let err { print(err.localizedDescription) }
// 關聯值提供上下文
// 協議組合:
// Equatable 比較錯誤

defer 清理

defer 作用域結束時執行。清理資源。逆序執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func processFile() throws {
print("打開文件")
// 延遲清理:
defer {
print("關閉文件")
}
print("處理數據")
// 中途出錯也會執行 defer
// throw SomeError()
print("正常結束")
}
try? processFile()
// 多個 defer 逆序:
defer { print("A") }
defer { print("B") }
// 輸出 B 後 A
// 用途:
// 關閉文件/連接
// 解鎖、恢復狀態
// 清理臨時資源
// 提前 return 也執行
// 注意:
// 不在 defer 中讀寫返回值

致命錯誤

fatalError 不可恢復崩潰。precondition 條件檢查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 不可恢復:
// fatalError("無法繼續")
// precondition:
func process(_ n: Int) {
precondition(n > 0, "參數必須為正數")
print(n)
}
process(5)
// process(-1) // 崩潰(precondition 不滿足)
// 調試斷言:
assert(true, "僅調試檢查")
// 其他:
// assert 調試構建生效
// precondition 都生效
// 適用場景:
// 不變量被破壞
// 程序無法繼續的 bug
// 開發期儘早失敗
// 生產用錯誤處理
// 不要濫用

Result 類型

Result<Success, Failure> 顯式成功或失敗。避免 throws。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Result 使用:
func fetchData() -> Result<[String], Error> {
let ok = true
if ok {
return .success(["a", "b"])
} else {
return .failure(NetworkError.timeout(30))
}
}
enum NetworkError: Error {
case timeout(Int)
}
// 處理:
let result = fetchData()
switch result {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
// 方法:
// result.map { ... }
// result.flatMap { ... }
// try result.get() 轉 throws
// 場景:
// 異步回調返回
// 非異常錯誤流
// 與 Result<_, Never> 組合
// 存儲錯誤結果數組

12.輸入輸出

命令行輸入、文件讀寫、FileManager 與 Codable。

讀取輸入

readLine 讀取命令行輸入。循環交互。解析數字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 讀取一行:
print("請輸入名字:", terminator: "")
if let name = readLine() {
print("你好,\(name)!")
}
// 讀取數字:
print("輸入年齡:", terminator: "")
if let input = readLine(), let age = Int(input) {
print("年齡 \(age)")
}
// 循環交互:
while let line = readLine(), !line.isEmpty {
print("收到: \(line)")
}
// 批量管道輸入:
// echo "data" | swift main.swift
// readLine 返回 nil 表示 EOF
// 標準錯誤輸出:
// FileHandle.standardError
// 輸出:
// print 到標準輸出

讀取文件

String(contentsOf:) 讀文件。Data 二進制。逐行讀取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 讀取為字符串:
let path = "data.txt"
do {
let content = try String(contentsOfFile: path, encoding: .utf8)
print(content)
} catch {
print("讀取失敗: \(error)")
}
// 讀取為 Data:
let data = try? Data(contentsOf: URL(fileURLWithPath: path))
// 逐行處理:
let text = "line1\nline2\n"
for line in text.components(separatedBy: .newlines) {
print(line)
}
// 相對路徑注意:
// 當前工作目錄
// 沙盒環境需 Document 目錄
// 編碼:
// .utf8 / .unicode
// 大文件流式讀取:
// FileHandle + read(upToCount:)

寫入文件

寫入字符串與 Data。追加模式。原子寫入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
let content = "hello\nworld\n"
// 覆蓋寫入:
let url = URL(fileURLWithPath: "out.txt")
try? content.write(to: url, atomically: true, encoding: .utf8)
// Data 寫入:
let data = Data(content.utf8)
try? data.write(to: url)
// 追加寫入:
if let handle = try? FileHandle(forWritingTo: url) {
handle.seekToEndOfFile()
handle.write(Data("more\n".utf8))
try? handle.close()
}
// 路徑處理:
let dir = URL(fileURLWithPath: "data")
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
// 錯誤處理:
// try? 忽略錯誤
// 生產用 do-catch
// 原子寫入防損壞

FileManager

文件系統操作。創建目錄、刪除、移動、判斷存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
let fm = FileManager.default
// 判斷存在:
let exists = fm.fileExists(atPath: "data.txt")
// 創建目錄:
try? fm.createDirectory(
atPath: "data/sub", withIntermediateDirectories: true)
// 移動/重命名:
try? fm.moveItem(atPath: "a.txt", toPath: "b.txt")
// 複製:
try? fm.copyItem(atPath: "b.txt", toPath: "c.txt")
// 刪除:
try? fm.removeItem(atPath: "c.txt")
// 列出目錄:
if let items = try? fm.contentsOfDirectory(atPath: ".") {
print(items)
}
// 屬性:
if let attrs = try? fm.attributesOfItem(atPath: "data.txt") {
print(attrs[.size] ?? "?")
}
// 目錄遍歷:
// enumerator(atPath:)
// 沙盒路徑:
// fm.urls(for: .documentDirectory, in: .userDomainMask)

輸出控制

print 變體。stderr。分隔與終止。字符串描述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 基礎:
print("普通輸出")
print(1, 2, 3) // 空格分隔
print("a", "b", separator: "-")
print("x", terminator: "") // 不換行
print() // 換行
// 標準錯誤:
FileHandle.standardError.write(
Data("錯誤信息\n".utf8))
// 調試描述:
debugPrint("debug")
// 自定義輸出:
struct Item: CustomStringConvertible {
var name: String
var description: String {
"Item(\(name))"
}
}
print(Item(name: "a"))
// CustomDebugStringConvertible:
// 調試更詳細
// dump 深度打印:
dump([1, 2, 3])

Data 處理

Data 字節容器。互轉字符串。base64 編解碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 字符串轉 Data:
let str = "hello"
let data = Data(str.utf8)
// Data 轉字符串:
let back = String(data: data, encoding: .utf8)
print(back ?? "")
// base64:
let encoded = data.base64EncodedString()
print(encoded) // aGVsbG8=
let decoded = Data(base64Encoded: encoded)
// 字節操作:
var bytes: [UInt8] = [0x68, 0x69]
let d = Data(bytes)
print(Array(d)) // [104, 105]
// 追加:
var buffer = Data()
buffer.append(Data([1, 2]))
buffer.append(Data("a".utf8))
// 十六進制:
let hex = data.map { String(format: "%02x", $0) }.joined()
print(hex) // 68656c6c6f
// 讀取子範圍:
// data.subdata(in: 0..<2)

JSONSerialization

JSON 轉字典/數組。反向序列化。JSONSerialization 類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// JSON 字符串:
let jsonStr = "{\"name\": \"Nick\", \"age\": 30, \"tags\": [\"dev\"]}"
let jsonData = Data(jsonStr.utf8)
// 解析為 Any:
let obj = try? JSONSerialization.jsonObject(
with: jsonData)
// 轉字典:
if let dict = obj as? [String: Any] {
print(dict["name"] ?? "")
let age = dict["age"] as? Int ?? 0
let tags = dict["tags"] as? [String] ?? []
print(age, tags)
}
// 反向序列化:
let payload: [String: Any] = [
"name": "Tom", "age": 25
]
if JSONSerialization.isValidJSONObject(payload) {
let data = try? JSONSerialization.data(withJSONObject: payload)
let str = String(data: data!, encoding: .utf8)
print(str ?? "")
}
// 現代推薦 Codable

Codable

Codable 自動序列化。JSONEncoder/Decoder。協議驅動。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 類型遵守 Codable:
struct User: Codable {
var name: String
var age: Int
var tags: [String]
}
// 編碼:
let user = User(name: "Nick", age: 30, tags: ["dev"])
let encoder = JSONEncoder()
let data = try? encoder.encode(user)
let json = String(data: data!, encoding: .utf8)
print(json ?? "")
// 解碼:
let jsonStr = "{\"name\": \"Tom\", \"age\": 25, \"tags\": []}"
let decoder = JSONDecoder()
if let u = try? decoder.decode(User.self, from: Data(jsonStr.utf8)) {
print(u.name)
}
// 字段映射:
// CodingKeys 重命名
// snake_case:
// decoder.keyDecodingStrategy = .convertFromSnakeCase
// 數組解碼:[User].self
// 日期處理需策略

13.常見誤區

Swift 新手最容易踩的坑與正確寫法。

強制解包

! 解包 nil 崩潰。優先可選綁定與空合運算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// BAD:強制解包
var name: String? = nil
// print(name!) // 崩潰
// GOOD:可選綁定
if let name = name {
print(name)
}
// GOOD:空合運算符
let display = name ?? "匿名"
print(display)
// GOOD:guard 提前解包
func show(_ v: String?) {
guard let v = v else {
print("無值")
return
}
print(v)
}
// 何時可安全 !:
// 確定非 nil(已賦值)
// 測試/原型
// 其餘避免
// try! 同理謹慎
// 崩潰信息難看
// 生產代碼剋制 !

可選鏈

?. 鏈式訪問。任一環節 nil 整體 nil。賦值也可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Address { var city = "北京" }
class Person {
var address: Address?
}
let p = Person()
// GOOD:可選鏈
let city = p.address?.city // String?
print(city ?? "無地址")
// BAD:假定中間非 nil
// p.address!.city // 可能崩潰
// 多層鏈:
// p.address?.city.count
// 賦值的可選鏈:
p.address?.city = "上海" // nil 時靜默
// 賦值地址:
p.address = Address()
p.address?.city = "上海"
print(p.address?.city ?? "")
// 方法調用可選鏈:
// p.address?.method()
// 判斷鏈中是否有值:
if let c = p.address?.city {
print(c)
}
// 常見誤區:
// 鏈返回可選類型
// 不會自動解包
// 與強制鏈 !. 對比

閉包循環引用

閉包捕獲 self 強引用形成環。捕獲列表 [weak self]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Loader {
var data: String = ""
var completion: (() -> Void)?
func setup() {
// BAD:閉包強捕獲 self
// completion = {
// self.data = "loaded"
// }
// GOOD:[weak self]
completion = { [weak self] in
self?.data = "loaded"
// self 釋放後自動 nil
}
}
deinit { print("Loader 釋放") }
}
// 使用:
var l: Loader? = Loader()
l?.setup()
// 執行閉包或釋放:
l = nil // 正常釋放
// 其他打破方式:
// [unowned self] 假定存在
// 閉包外強引用後手動 nil
// 常見於:
// 網絡回調、Timer、動畫

數組越界

索引越界崩潰。安全訪問 first/last/prefix。檢查邊界。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let nums = [1, 2, 3]
// BAD:越界索引
// print(nums[5]) // 崩潰
// GOOD:安全訪問
if nums.indices.contains(5) {
print(nums[5])
}
// 安全方法:
let first = nums.first ?? 0
let last = nums.last ?? 0
// 安全取元素擴展:
extension Array {
func safe(_ index: Int) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
print(nums.safe(1) ?? 0) // 2
print(nums.safe(9) ?? 0) // 0
// 空數組注意:
// nums[0] 也崩潰
// 遍歷用 indices 或 enumerated
// 動態索引先檢查

mutating

struct 方法修改屬性需 mutating。值類型不可變副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct Counter {
var count = 0
// 需要修改自身:
mutating func increment() {
count += 1
}
// 只讀方法不需要:
func current() -> Int {
count
}
}
var c = Counter()
c.increment()
c.increment()
print(c.current()) // 2
// BAD:常量實例調 mutating
// let cc = Counter()
// cc.increment() // 錯誤
// GOOD:var 變量實例
var cc2 = Counter()
cc2.increment()
// 原因:
// mutating 修改 self
// 常量實例不可變
// 規則:
// struct/enum 需要 mutating
// class 不需要
// 下標、協議方法也適用
// 值語義的體現

字符串索引

字符串不能整數下標。字符多字節。用 index 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let s = "你好 Swift"
// BAD:整數下標
// let c = s[0] // 錯誤
// 字符數不等於字節數
// GOOD:索引 API
let first = s.startIndex
let c = s[first] // 你
print(c)
// 偏移:
let second = s.index(after: first)
print(s[second])
// 指定偏移:
let idx = s.index(s.startIndex, offsetBy: 3)
print(s[idx])
// 字符數組轉換:
let chars = Array(s)
print(chars[0]) // 你
// 遍歷:
for c in s { print(c) }
// 切片:
// s[..<idx]
// 多字節安全
// String.Index 保證邊界

協議與 Self

協議中使用 Self 約束。associatedtype。類型擦除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 協議 Self 約束:
protocol EquatableLike {
func isEqualTo(_ other: Self) -> Bool
}
// 遵守:
struct Box: EquatableLike {
var value: Int
func isEqualTo(_ other: Box) -> Bool {
self.value == other.value
}
}
// BAD:把含 Self 協議當類型
// func compare(a: EquatableLike, b: EquatableLike) // 錯誤
// GOOD:泛型約束
func compare<T: EquatableLike>(_ a: T, _ b: T) -> Bool {
a.isEqualTo(b)
}
// 關聯類型:
// protocol Container {
// associatedtype Item
// }
// 類型擦除:
// AnyEquatable 包裝
// 需要動態多態時
// 用泛型或類型擦除

隱式解包

T! 隱式解包可選。使用未初始化崩潰。謹慎使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// BAD:隱式解包訪問未賦值崩潰
var name: String! = nil
// 使用直接:
// print(name) // 崩潰(未賦值)
// 賦值後:
name = "Nick"
print(name) // 自動解包
// 用途:
// IBOutlet 連接
// 初始化後必然有值
// 兩階段初始化
// 風險:
// 訪問未賦值時崩潰
// 丟失可選安全性
// 不建議新代碼使用
// GOOD:普通可選
var safeName: String?
// 解包:
if let n = safeName {
print(n)
}
// 強制場景:
// 確定初始化後再訪問
// 或改為 let 常量

14.併發

GCD、async/await、Task 與 actor 併發模型。

GCD 基礎

DispatchQueue 串行/併發隊列。全局與主隊列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 主隊列:
let main = DispatchQueue.main
// 全局併發隊列:
let background = DispatchQueue.global(qos: .background)
// 創建串行隊列:
let serial = DispatchQueue(label: "com.app.serial")
// 創建併發隊列:
let concurrent = DispatchQueue(
label: "com.app.concurrent", attributes: .concurrent)
// 提交任務:
background.async {
// 後台執行...
print("後台任務")
}
// 同步提交:
// serial.sync { ... }
// 延遲執行:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("1 秒後")
}
// QoS 優先級:
// .userInteractive / .userInitiated
// .utility / .background

調度組

DispatchGroup 等待多個任務完成。批量併發彙總。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
let group = DispatchGroup()
let queue = DispatchQueue.global()
// 進入組:
// 多次異步任務
for i in 0..<3 {
group.enter()
queue.async {
print("任務 \(i) 執行")
// 模擬耗時
Thread.sleep(forTimeInterval: 0.2)
group.leave()
}
}
// 全部完成回調:
group.notify(queue: .main) {
print("所有任務完成")
}
// 同步等待:
// group.wait()
// 超時等待:
// group.wait(timeout: .now() + 5)
// 注意:
// enter 與 leave 必須成對
// 多線程安全
// 適合並行下載/批量處理

async/await

現代 Swift 併發語法。異步函數與掛起。結構化併發。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 異步函數:
func fetchData() async -> String {
// 模擬網絡延遲
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "數據"
}
// 調用:
func load() async {
let data = await fetchData()
print(data)
}
// 異步拋錯函數:
func risky() async throws -> Int {
try await Task.sleep(nanoseconds: 100_000_000)
return 42
}
// 調用:
func caller() async {
if let v = try? await risky() {
print(v)
}
}
// 注意:
// 異步函數不可在同步上下文調用
// Task { } 包一層
// 與舊回調風格互轉

async let

併發執行多個異步調用。綁定獨立子任務。並行等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 三個獨立異步任務:
func loadUser() async -> String { "用户" }
func loadPosts() async -> String { "帖子" }
func loadComments() async -> String { "評論" }
// async let 併發:
func loadAll() async -> String {
async let user = loadUser()
async let posts = loadPosts()
async let comments = loadComments()
// 全部並行執行:
let result = await "\(user) \(posts) \(comments)"
return result
}
// 使用:
// 三個任務同時跑
// await 聚合所有結果
// 注意:
// async let 綁定後必須 await
// 依賴關係:
// 有依賴用順序 await
// 無依賴用 async let
// 適合並行請求獨立接口

Task

Task 創建併發單元。繼承上下文。取消與優先級。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Foundation
// 創建任務:
func run() async {
let task = Task {
try await Task.sleep(nanoseconds: 500_000_000)
return "完成"
}
let result = await task.value // String
print(result)
}
// 派發到主線程:
// @MainActor func updateUI()
// Task { @MainActor in ... }
// 任務取消:
task.cancel()
// 檢查取消:
try Task.checkCancellation()
// 結構化任務組:
await withTaskGroup(of: String.self) { group in
for i in 0..<3 {
group.addTask {
"任務\(i)"
}
}
for await r in group {
print(r)
}
}
// 優先級:Task(priority: .high)

actor

actor 隔離狀態。避免數據競爭。異步方法訪問。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import Foundation
// 定義 actor:
actor BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount
}
func getBalance() -> Double {
balance
}
}
// 使用(必須 await):
func test() async {
let account = BankAccount()
// 訪問隔離狀態需 await:
await account.deposit(100)
let balance = await account.getBalance()
print(balance)
}
// 併發安全:
// 編譯器保證串行訪問
// 避免數據競爭
// 對比:
// 類需自己加鎖
// actor 自動隔離
// 注意:
// actor 方法默認 async 化
// 需 await 調用
// 適合共享可變狀態

主線程

UI 操作必須在主線程。線程跳轉。主線程檢查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 判斷主線程:
if Thread.isMainThread {
print("在主線程")
}
// 回到主線程:
DispatchQueue.main.async {
// UI 更新
print("主線程執行")
}
// 等待主線程執行:
// DispatchQueue.main.sync { }
// 注意死鎖:
// 主線程 sync 到自己會死鎖
// 現代 Swift:
@MainActor
func updateUI() {
// 隔離到主線程
}
// 從異步上下文調用:
// await MainActor.run { ... }
// 規則:
// UI 更新、動畫主線程
// 耗時任務放後台
// 後台回主線程更新

線程安全

可變狀態併發訪問保護。鎖、隊列、原子操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import Foundation
// 用串行隊列保護:
final class SafeCounter {
private let queue = DispatchQueue(label: "counter")
private var _count = 0
var count: Int {
queue.sync { _count }
}
func increment() {
queue.sync { _count += 1 }
}
}
let counter = SafeCounter()
for _ in 0..<100 {
DispatchQueue.global().async {
counter.increment()
}
}
// 鎖:
// NSLock
let lock = NSLock()
lock.lock()
// 臨界區...
lock.unlock()
// 原子:
// OSAllocatedUnfairLock
// 現代方案:
// actor 隔離最安全
// 只讀常量無需保護
// 優先不可變數據

15.網絡

URLSession、HTTP 請求、異步網絡與 JSON 交互。

URLSession

URLSession 發起網絡請求。配置與委託。會話類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 默認會話:
let session = URLSession.shared
// 自定義配置:
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
config.waitsForConnectivity = true
let custom = URLSession(configuration: config)
// 請求:
let url = URL(string: "https://api.example.com")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
// 會話類型:
// .default / .ephemeral / .background
// ephemeral 不存緩存 Cookie
// background 後台傳輸
// 數據任務:
let task = session.dataTask(with: request)
// 配合 async/await 使用

GET 請求

發起 GET。async/await 獲取數據。處理響應狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 異步 GET:
func fetchContent() async throws -> String {
let url = URL(string: "https://api.example.com/data")!
let (data, response) = try await URLSession.shared.data(from: url)
// 檢查狀態碼:
guard let http = response as? HTTPURLResponse,
http.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return String(data: data, encoding: .utf8) ?? ""
}
// 使用:
func load() async {
do {
let content = try await fetchContent()
print(content)
} catch {
print("請求失敗: \(error)")
}
}
// 查詢參數:
var comps = URLComponents(string: "https://api.example.com/search")!
comps.queryItems = [URLQueryItem(name: "q", value: "swift")]
// 超時:
// request.timeoutInterval = 10
// 舊回調式:
// dataTask(with:completionHandler:)

POST 請求

發送 JSON body。表單提交。設置 Content-Type。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// POST JSON:
func postJSON() async throws {
let url = URL(string: "https://api.example.com/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// body:
let payload: [String: Any] = ["name": "Nick", "age": 30]
request.httpBody = try JSONSerialization.data(withJSONObject: payload)
let (data, response) = try await URLSession.shared.data(for: request)
// 處理響應...
print(response)
}
// 表單:
var form = URLRequest(url: URL(string: "https://x/login")!)
form.httpMethod = "POST"
form.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
form.httpBody = "user=nick&pwd=123".data(using: .utf8)
// 注意:
// 大 body 用流式
// 響應同樣檢查狀態碼
// Codable 直接編碼

異步網絡

async/await 網絡調用。併發請求。取消任務。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 異步請求 + Codable:
struct User: Codable { var id: Int; var name: String }
func fetchUsers() async throws -> [User] {
let url = URL(string: "https://api.example.com/users")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([User].self, from: data)
}
// 併發多個請求:
func loadAll() async throws -> (User, [String]) {
async let user = fetchUsers()
async let tags: [String] = ["a", "b"]
return (try await user.first!, try await tags)
}
// 任務取消:
// Task 取消時網絡請求自動中斷
// 響應檢查
func fetchWithTimeout() async throws -> Data {
let url = URL(string: "https://api.example.com")!
// 超時:
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForRequest = 10
let session = URLSession(configuration: config)
return try await session.data(from: url).0
}
// 錯誤處理:
// URLError 類型分類

URL 構建

URLComponents 安全構建 URL。查詢參數編碼。路徑組合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 基礎 URL:
var comps = URLComponents()
comps.scheme = "https"
comps.host = "api.example.com"
comps.path = "/v1/search"
// 查詢參數:
comps.queryItems = [
URLQueryItem(name: "q", value: "swift 併發"),
URLQueryItem(name: "limit", value: "10"),
URLQueryItem(name: "page", value: "2"),
]
// 自動編碼:
let url = comps.url!
print(url.absoluteString)
// 解析 URL:
if let c = URLComponents(string: url.absoluteString) {
print(c.host ?? "")
for item in c.queryItems ?? [] {
print("\(item.name)=\(item.value ?? "")")
}
}
// 好處:
// 自動百分號編碼
// 中文/特殊字符安全
// 避免字符串拼接錯誤
// 添加片段:
comps.fragment = "section"

下載文件

下載任務保存文件。進度監聽。後台下載。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
// 數據下載:
func downloadImage() async throws -> Data {
let url = URL(string: "https://example.com/img.png")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
// 保存到文件:
func save() async throws {
let data = try await downloadImage()
let dir = FileManager.default.temporaryDirectory
let file = dir.appendingPathComponent("img.png")
try data.write(to: file)
print(file.path)
}
// 文件下載任務:
let config = URLSessionConfiguration.background(withIdentifier: "dl")
let session = URLSession(configuration: config)
// 進度監聽:
// 委託代理 URLSessionDownloadDelegate
// 下載完成回調
// 斷點續傳
// background 支持
// 注意:
// 大文件用磁盤寫入
// 沙盒目錄選擇正確
// 內存考慮

網絡 JSON

請求 JSON 並用 Codable 解碼。日期與策略處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 模型:
struct Post: Codable {
var id: Int
var title: String
var created: Date
}
// 日期解碼策略:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
// 蛇形命名:
// decoder.keyDecodingStrategy = .convertFromSnakeCase
// 請求:
func fetchPosts() async throws -> [Post] {
let url = URL(string: "https://api.example.com/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
return try decoder.decode([Post].self, from: data)
}
// 編碼上傳:
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
// 錯誤處理:
// DecodingError 分類
// .dataCorrupted / .keyNotFound
// 網絡層錯誤分開處理
// 模型驗證

上傳文件

multipart 表單上傳。文件 body。邊界分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// multipart 上傳:
func upload(data: Data, filename: String) async throws {
let url = URL(string: "https://api.example.com/upload")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue(
"multipart/form-data; boundary=\(boundary)",
forHTTPHeaderField: "Content-Type")
// 構建 body:
var body = Data()
body.append("--\(boundary)\r\n".data(using: .utf8)!)
let header = "Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n\r\n"
body.append(header.data(using: .utf8)!)
body.append(data)
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (_, response) = try await URLSession.shared.data(for: request)
print(response)
}
// 注意:
// boundary 唯一
// 大文件流式上傳
// 進度用 URLSessionUploadTask
// 服務器響應校驗

16.時間與日期

Date、DateFormatter、日曆運算與計時器。

Date

Date 表示時間點。時區無關的絕對時刻。創建與比較。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 當前時間:
let now = Date()
// 時間戳(秒):
let ts = now.timeIntervalSince1970
print(ts)
// 從時間戳創建:
let fromTs = Date(timeIntervalSince1970: 1700000000)
// 偏移創建:
let inOneHour = Date().addingTimeInterval(3600)
// 比較:
let earlier = Date(timeIntervalSince1970: 0)
print(now > earlier) // true
// 時間間隔:
let diff = now.timeIntervalSince(earlier)
print(diff) // 秒
// 排序:
let dates = [now, earlier].sorted()
// 注意:
// Date 與地區無關
// 展示需 DateFormatter
// 運算用 Calendar
// 不要手動加減秒

日期格式化

DateFormatter 顯示日期。本地化格式。自定義格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
let now = Date()
let formatter = DateFormatter()
// 本地化:
formatter.locale = Locale(identifier: "zh_CN")
formatter.timeZone = .current
// 預設風格:
formatter.dateStyle = .medium // 2026年8月2日
formatter.timeStyle = .short // 上午10:30
print(formatter.string(from: now))
// 自定義格式:
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(formatter.string(from: now))
// 格式符號:
// yyyy 年 / MM 月 / dd 日
// HH 24時 / mm 分 / ss 秒
// EEEE 星期全名
formatter.dateFormat = "yyyy年MM月dd日 EEEE"
print(formatter.string(from: now))
// 注意:
// 固定格式時設置 locale
// 展示優先預設風格
// 12/24 小時制

解析日期

字符串轉 Date。固定格式解析。ISO8601 解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 自定義格式解析:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = .current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.date(from: "2026-08-02 10:30:00") {
print(date)
}
// ISO8601 解析:
let iso = ISO8601DateFormatter()
if let d = iso.date(from: "2026-08-02T10:30:00Z") {
print(d)
}
// 鬆散解析:
formatter.dateFormat = "yyyy/M/d"
// 注意:
// 解析失敗返回 nil
// 固定格式用 en_US_POSIX
// 避免歧義
// 輸入格式不一致時
// 多個 formatter 嘗試
// 服務器 ISO 優先 iso8601

日期組件

DateComponents 提取年月日。日曆組件運算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
let calendar = Calendar.current
let now = Date()
// 提取組件:
let comps = calendar.dateComponents(
[.year, .month, .day, .weekday], from: now)
print(comps.year ?? 0, comps.month ?? 0, comps.day ?? 0)
print(comps.weekday ?? 0) // 1=週日...7=週六
// 單獨提取:
let year = calendar.component(.year, from: now)
// 組件創建日期:
var c = DateComponents()
c.year = 2026
c.month = 8
c.day = 2
let date = calendar.date(from: c)
print(date ?? Date())
// 今天開始:
let start = calendar.startOfDay(for: now)
// 注意:
// 組件用日曆語義
// 跨時區安全
// 代替手算日期

日曆運算

日期加減天月。下週/下月。Calendar 運算安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
let calendar = Calendar.current
let now = Date()
// 加天數:
let tomorrow = calendar.date(byAdding: .day, value: 1, to: now)!
// 加月:
let nextMonth = calendar.date(byAdding: .month, value: 1, to: now)!
// 加年:
let nextYear = calendar.date(byAdding: .year, value: 1, to: now)!
// 月初:
let monthStart = calendar.date(from:
calendar.dateComponents([.year, .month], from: now))!
// 下一個週一:
let monday = calendar.nextDate(
after: now, matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime)!
// 日期間隔:
let days = calendar.dateComponents(
[.day], from: now, to: tomorrow).day!
print(days) // 1
// 判斷同日:
calendar.isDate(now, inSameDayAs: tomorrow)
// 時區:
// calendar.timeZone

時間間隔

測量代碼耗時。TimeInterval 秒單位。性能計時。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 計時:
let start = Date()
// 執行任務...
Thread.sleep(forTimeInterval: 0.3)
let elapsed = Date().timeIntervalSince(start)
print("\(elapsed) 秒") // ~0.3
// 高精度計時:
let t0 = DispatchTime.now()
// 任務...
let t1 = DispatchTime.now()
let nanos = t1.uptimeNanoseconds - t0.uptimeNanoseconds
print("\(Double(nanos) / 1e6) ms")
// 休眠:
Thread.sleep(forTimeInterval: 0.5)
// 異步休眠:
// try await Task.sleep(nanoseconds: 500_000_000)
// TimeInterval = Double 秒
// 注意:
// 預熱忽略首次
// 多次取平均
// 單位統一秒

Timer

定時器重複執行。一次性定時。RunLoop 注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 重複定時器:
var count = 0
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
count += 1
print("tick \(count)")
if count >= 3 {
timer.invalidate() // 停止
}
}
// 一次性:
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) {
print("2 秒後執行")
}
// 注意:
// scheduledTimer 加入當前 RunLoop
// 主線程可用
// 後台線程需 run loop
// 無效:
// timer.invalidate()
// 避免強引用:
// 閉包捕獲 self 循環
// 用 [weak self]
// 現代替代:
// Task.sleep 循環

日期比較

比較日期先後。判斷同一天。排序與驗證。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
let calendar = Calendar.current
let now = Date()
let past = Date(timeIntervalSinceNow: -3600)
let future = Date(timeIntervalSinceNow: 3600)
// 直接比較:
print(future > now) // true
// 同一天:
calendar.isDate(now, inSameDayAs: future)
// 今天/昨天/明天:
calendar.isDateInToday(now)
calendar.isDateInYesterday(past)
calendar.isDateInTomorrow(future)
// 區間包含:
let range = past...future
print(range.contains(now)) // true
// 排序:
let dates = [future, past, now].sorted()
// 差值天數:
let daysBetween = calendar.dateComponents(
[.day], from: past, to: future).day ?? 0
print(daysBetween)
// 驗證過期:
if let expires = Calendar.current.date(byAdding: .hour, value: 2, to: now),
now > expires {
print("已過期")
}

17.進程與系統

命令行參數、環境變量、進程操作與文件路徑。

命令行參數

CommandLine 獲取參數。處理參數與選項。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
// 獲取參數:
let args = CommandLine.arguments
print(args) // ["程序名", "arg1", "arg2"]
// 參數個數:
let count = CommandLine.arguments.count
// 簡單解析:
let arguments = CommandLine.arguments
var verbose = false
var inputFile = ""
var index = 1
while index < arguments.count {
switch arguments[index] {
case "-v", "--verbose":
verbose = true
case "-f", "--file":
index += 1
inputFile = arguments[index]
default:
print("未知參數: \(arguments[index])")
}
index += 1
}
print("verbose=\(verbose), file=\(inputFile)")
// 注意:
// 第一個是程序路徑
// 參數用 CommandLine.arguments
// 生產可用 ArgumentParser 庫

環境變量

讀取環境變量。設置與傳遞。敏感信息注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
// 讀取環境變量:
if let home = ProcessInfo.processInfo.environment["HOME"] {
print(home)
}
// 檢查存在:
let hasKey = ProcessInfo.processInfo.environment["API_KEY"] != nil
// 全部變量:
for (key, value) in ProcessInfo.processInfo.environment {
print("\(key)=\(value)")
}
// 默認值:
let port = ProcessInfo.processInfo.environment["PORT"] ?? "8080"
print(port)
// 系統信息:
let os = ProcessInfo.processInfo.operatingSystemVersionString
let cores = ProcessInfo.processInfo.activeProcessorCount
print(os, cores)
// 安全注意:
// 密鑰放環境變量
// 不提交到代碼庫
// 敏感值避免打印

Process

Process 啓動子進程。執行命令。捕獲輸出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 運行命令:
func run(_ cmd: String, _ args: [String]) throws -> String {
let process = Process()
process.executableURL = URL(fileURLWithPath: cmd)
process.arguments = args
// 捕獲輸出:
let pipe = Pipe()
process.standardOutput = pipe
try process.run()
process.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: .utf8) ?? ""
}
// 使用:
try print(run("/usr/bin/env", ["date"]))
// 環境變量:
process.environment = ["PATH": "/usr/bin"]
// 工作目錄:
process.currentDirectoryURL = URL(fileURLWithPath: "/tmp")
// 退出碼:
let status = process.terminationStatus
// 注意:
// 路徑要正確
// 長命令用字符串拆分
// 異步用 terminationHandler

路徑處理

URL 路徑操作。組合、擴展名、文件名。沙盒目錄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import Foundation
let fileURL = URL(fileURLWithPath: "/tmp/data/file.txt")
// 文件名:
let name = fileURL.lastPathComponent // "file.txt"
// 去掉擴展名:
let base = fileURL.deletingPathExtension().lastPathComponent // "file"
// 擴展名:
let ext = fileURL.pathExtension // "txt"
// 父目錄:
let dir = fileURL.deletingLastPathComponent
// 拼接:
let newFile = dir.appendingPathComponent("out.txt")
// 追加擴展名:
let md = fileURL.appendingPathExtension("md")
// 沙盒目錄:
let docs = FileManager.default.urls(
for: .documentDirectory, in: .userDomainMask)[0]
let caches = FileManager.default.temporaryDirectory
// 判斷標準:
// fileURL.hasDirectoryPath
// 相對路徑轉絕對:
// URL(fileURLWithPath: "a.txt").standardizedFileURL

退出碼

程序退出碼。exit 與 fatalError。0 成功非 0 失敗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 正常退出:
exit(0)
// 失敗退出:
// exit(1)
// 帶錯誤信息:
// 輸出到 stderr
// FileHandle.standardError.write(
// Data("錯誤\n".utf8))
// exit(2)
// 常用約定:
// 0 成功
// 1 一般錯誤
// 2 用法錯誤
// 127 命令未找到
// 判斷子進程狀態:
let process = Process()
// process.waitUntilExit()
// let code = process.terminationStatus
// print(code)
// 注意:
// exit 立即終止
// 不執行 defer
// 腳本中可用
// 正常結束不調 exit
// 返回 Int 主函數

信號處理

捕獲系統信號。SIGINT 等。優雅退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Foundation
import Darwin
// 信號處理:
signal(SIGINT) { _ in
print("收到 Ctrl-C")
exit(0)
}
// 忽略信號:
// signal(SIGHUP, SIG_IGN)
// 信號列表:
// SIGINT 中斷 (2)
// SIGTERM 終止 (15)
// SIGKILL 強殺 (9) 不可捕獲
// 優雅退出模式:
var running = true
signal(SIGTERM) { _ in
running = false
}
// while running {
// // 主循環...
// sleep(1)
// }
// 注意:
// 信號處理器要簡單
// 用原子變量通信
// DispatchSource 更現代:
// DispatchSource.makeSignalSource
// 異常退出不推薦

工作目錄

當前工作目錄獲取與切換。文件相對路徑基準。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
// 當前工作目錄:
let cwd = FileManager.default.currentDirectoryPath
print(cwd)
// 切換目錄:
// FileManager.default.changeCurrentDirectoryPath("/tmp")
// 相對路徑解析:
let rel = URL(fileURLWithPath: "data/config.json")
print(rel.path) // 基於當前目錄
// 標準化:
let normalized = rel.standardizedFileURL
// 判斷路徑:
let isAbsolute = rel.isFileURL
// 常用位置:
let home = FileManager.default.homeDirectoryForCurrentUser
let temp = FileManager.default.temporaryDirectory
// 注意:
// 工作目錄影響相對路徑
// 沙盒環境不同
// GUI 應用注意:
// 工作目錄可能是根
// 顯式構建絕對路徑更穩
// 配置用相對基準

進程文件 I/O

標準輸入讀取。標準輸出寫入。管道交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foundation
// 讀取標準輸入:
if let line = readLine() {
print("收到: \(line)")
}
// 循環讀取:
// while let line = readLine() {
// print(line)
// }
// 標準輸出:
print("輸出到 stdout")
// 標準錯誤:
FileHandle.standardError.write(
Data("錯誤信息\n".utf8))
// 從文件重定向:
// swift main.swift < input.txt
// 管道輸入:
// echo "data" | swift main.swift
// 輸出重定向:
// swift main.swift > out.txt 2>&1
// 注意:
// readLine 返回 nil 表示 EOF
// 大輸入注意性能
// 二進制用 FileHandle.standardInput

18.正則表達式

正則字面量、NSRegularExpression 與模式匹配。

正則基礎

正則語法概念。字符類、量詞、分組。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 基本語法:
// 字面量:/abc/ 匹配 abc
// 字符類:[abc] [0-9] \\d
// 量詞:* + ? {n,m}
// 錨點:^ $ \\b
// 分組:(...) 捕獲
// 或:a|b
// Swift 5.7+ 正則字面量:
let pattern = /\\d{4}-\\d{2}-\\d{2}/
let text = "日期 2026-08-02 結束"
if let match = text.firstMatch(of: pattern) {
print(match.0) // 2026-08-02
}
// 常用:
// \\d 數字 \\w 單詞 \\s 空白
// \\D \\W \\S 反向
// 貪婪 vs 懶惰:
// a.*b 貪婪 / a.*?b 懶惰
// 轉義:\\\\. 匹配點號

正則字面量

Swift 5.7 正則字面量 /.../。類型化匹配。捕獲命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 正則字面量:
let pattern = /h(\\d+)x/
// 類型化捕獲:
let text = "h123x h99x"
if let match = text.firstMatch(of: pattern) {
print(match.0) // h123x
print(match.1) // 123 (Substring)
}
// 命名捕獲:
let named = /(?<code>\\d{3})-(?<area>\\d{4})/
if let m = "123-4567".firstMatch(of: named) {
print(m.code) // 123
print(m.area) // 4567
}
// 選項:
let caseInsensitive = /abc/ .ignoresCase()
// 查找全部:
for m in text.matches(of: pattern) {
print(m.0)
}
// 注意:
// 類型安全編譯期校驗
// 與 NSRegularExpression 互轉
// 需 macOS 13 / iOS 16+

NSRegularExpression

NSRegularExpression 正則引擎。範圍匹配。兼容舊系統。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 創建:
let pattern = "\\d+"
let regex = try! NSRegularExpression(pattern: pattern)
// 匹配:
let text = "價格 100 元,折扣 20 元"
let range = NSRange(text.startIndex..., in: text)
// 全部匹配:
let matches = regex.matches(in: text, range: range)
for m in matches {
if let r = Range(m.range, in: text) {
print(text[r])
}
}
// 捕獲組:
let groupRegex = try! NSRegularExpression(pattern: "(\\d+)-(\\d+)")
// 首匹配:
if let first = groupRegex.firstMatch(in: text, range: range) {
// first.range(at: 1) 第一組
}
// 替換:
let replaced = regex.stringByReplacingMatches(
in: text, range: range, withTemplate: "#")
print(replaced)
// 選項:
// .caseInsensitive / .anchorsMatchLines

匹配

首次匹配、全部匹配、判斷是否匹配。範圍獲取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 判斷是否匹配:
let email = "[email protected]"
// 完整匹配:
let full = /[a-z]+@[a-z]+\\.[a-z]+/
print(email.wholeMatch(of: full) != nil)
// 部分包含:
print(email.contains(/@/))
// 首次匹配:
let pattern = /\\d+/
if let m = "abc123def".firstMatch(of: pattern) {
print(m.0) // 123
}
// 全部匹配:
let all = "a1b2c3".matches(of: /\\d+/)
print(all.count) // 3
// 匹配位置:
// m.range 返回範圍
// 前綴匹配:
if "2026-08".hasPrefix("2026") {}
// 注意:
// wholeMatch 完整匹配
// firstMatch 首次
// matches 全部
// 空匹配注意

捕獲組

提取捕獲組內容。命名捕獲。正則分組引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Foundation
// 捕獲組:
let pattern = /(\\d{4})-(\\d{2})-(\\d{2})/
let text = "2026-08-02"
if let m = text.wholeMatch(of: pattern) {
print(m.0) // 2026-08-02
print(m.1) // 2026
print(m.2) // 08
print(m.3) // 02
}
// 命名捕獲:
let named = /(?<year>\\d{4})-(?<month>\\d{2})/
if let m = "2026-08".wholeMatch(of: named) {
print(m.year) // 2026
print(m.month) // 08
}
// 可選捕獲:
// (x)? 匹配則可選
// 非捕獲組:
// (?:x) 不捕獲
// NSRegularExpression:
// m.range(at: 1) 提取組
// 用途:
// 解析結構化文本
// 提取 key=value

替換

正則替換文本。模板引用捕獲組。條件替換。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
// 字面替換:
let s = "Hello, world!"
let replaced = s.replacingOccurrences(of: "o", with: "0")
print(replaced) // Hell0, w0rld!
// 正則替換:
let text = "日期: 2026-08-02"
let regex = try! NSRegularExpression(pattern: "(\\d{4})-(\\d{2})-(\\d{2})")
let nsRange = NSRange(text.startIndex..., in: text)
// 模板引用組:
let formatted = regex.stringByReplacingMatches(
in: text, range: nsRange,
withTemplate: "$2/$3/$1")
print(formatted) // 日期: 08/02/2026
// 替換範圍:
let trimmed = text.replacingOccurrences(
of: "\\s+", with: " ", options: .regularExpression)
// 模板特殊字符:
// $0 全匹配 $1 組1
// 條件替換需循環匹配

分割

按正則分割字符串。分隔符保留與刪除。多分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Foundation
// 字符串分割:
let csv = "a,b,c"
let parts = csv.split(separator: ",")
print(parts) // ["a", "b", "c"]
// 正則分割:
let text = "one two three"
let words = text.components(separatedBy: .whitespacesAndNewlines)
// 多分隔符:
let messy = "a;b,c|d"
let regex = try! NSRegularExpression(pattern: "[;,_|]+")
let splitParts = regex.split(messy)
// 保留空串:
// split 默認刪空
// omittingEmptySubsequences
// 按行分割:
let lines = text.components(separatedBy: .newlines)
// 注意:
// 分隔符正則
// 拆分時去掉分隔符
// 大文本注意性能

常用模式

郵箱、URL、數字、手機號等常用正則模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
// 郵箱:
let email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/
// URL:
let url = /https?:\\/\\/[\\w.-]+(?:\\/[\\w._-]*)*/
// 整數:
let int = /^[+-]?\\d+$/
// 浮點數:
let float = /^[+-]?\\d+\\.\\d+$/
// 手機號:
let phone = /^1[3-9]\\d{9}$/
// 身份證:
let idCard = /^\\d{17}[0-9Xx]$/
// IP 地址:
let ip = /(?:\\d{1,3}\\.){3}\\d{1,3}/
// 中文:
let chinese = /[\\u4e00-\\u9fa5]+/
// 空格:
let spaces = /\\s+/
// 使用:
print("[email protected]".wholeMatch(of: email) != nil)
print("13800138000".wholeMatch(of: phone) != nil)
// 注意:
// 正則要經過測試
// 性能:複雜模式慎用
// 實際校驗用專門的庫

19.構建與工具

swiftc 編譯、SwiftPM 構建、格式與檢查工具。

swiftc 編譯

命令行編譯單文件。生成可執行。優化選項。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 編譯執行文件:
# swiftc main.swift -o app
# 運行:
# ./app
# 直接解釋:
# swift main.swift
# 多個源文件:
# swiftc main.swift utils.swift -o app
# 生成模塊:
# swiftc -emit-module
# 優化:
# -O 優化編譯
# -Ounchecked 更快但不檢查
# -Onone 調試
# 調試信息:
# -g 生成調試符號
# 鏈接框架:
# -framework Foundation
# 交叉編譯:
# -target x86_64-unknown-linux-gnu
# 查看版本:
# swift --version
# 説明:
# SwiftPM 管理多文件
# 單文件開發用 swiftc

SwiftPM 構建

swift build 編譯項目。增量構建。發佈模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 初始化項目:
# swift package init --type executable
# 構建:
# swift build
# 發佈模式:
# swift build -c release
# 指定產品:
# swift build --product app
# 增量構建(默認)
# 清理:
# swift package clean
# 更新依賴:
# swift package resolve
# 查看依賴樹:
# swift package show-dependencies
# 輸出位置:
# .build/debug/ 和 .build/release/
# 運行可執行:
# .build/debug/myapp
# 説明:
# Package.swift 聲明包
# 依賴用 git 地址/版本
# 構建產物在 .build/

運行測試

swift test 測試套件。XCTest 與測試目標。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 運行測試:
# swift test
# 指定測試:
# swift test --filter TestMath
# 單個用例:
# swift test --filter TestMath/testAdd
# 並行:
# swift test --parallel
# 代碼覆蓋:
# swift test --enable-code-coverage
# 生成覆蓋報告:
# llvm-cov report .build/debug/*.xctest
# 測試文件:
import XCTest
final class TestMath: XCTestCase {
func testAdd() {
XCTAssertEqual(add(1, 2), 3)
}
}
# 斷言:
# XCTAssertTrue / XCTAssertNil
# 異步測試:
# XCTestExpectation
# 説明:
# 測試目標在 Package.swift 聲明
# 命名 testXxx 自動發現

Package.swift

SwiftPM 包清單。目標與依賴聲明。平台配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MyApp",
// 平台:
platforms: [
.macOS(.v13)
],
// 產品:
products: [
.executable(name: "app", targets: ["App"]),
.library(name: "Core", targets: ["Core"]),
],
// 依賴:
dependencies: [
.package(url: "https://github.com/xxx/yyy", from: "1.0.0"),
],
// 目標:
targets: [
.executableTarget(
name: "App",
dependencies: ["Core", "yyy"]),
.target(name: "Core"),
.testTarget(
name: "AppTests",
dependencies: ["App"]),
]
)
# 依賴版本:
# from: 1.0.0 向上兼容
# exact: 1.2.3 精確
# branch: main

SwiftLint

代碼風格檢查。配置規則。CI 集成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 安裝(Homebrew):
# brew install swiftlint
# 運行:
# swiftlint
# 修復:
# swiftlint autocorrect
# 指定目錄:
# swiftlint lint --path Sources/
# 配置文件:
# 生成默認配置
# swiftlint generate-docs
# 常見規則:
# line_length 行長
# trailing_whitespace 尾隨空格
# force_cast 強制轉換
# force_unwrapping 強制解包
# 配置 .swiftlint.yml:
# disabled_rules:
# - force_cast
# opt_in_rules:
# - empty_count
# 忽略文件:
# excluded:
# - Generated/
# CI 集成:
# 失敗則構建失敗
# 保證風格一致

swift-format

官方代碼格式化工具。格式規則與配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 格式化:
# swift-format format -i Sources/*.swift
# 檢查:
# swift-format lint -r Sources/
# 配置:
# swift-format dump-configuration > .swift-format
# 與 Xcode 集成
# 配置示例:
# {
# "indentation": {
# "spaces": 4
# },
# "lineBreakBeforeEachArgument": false,
# "rules": {
# "AllPublicDeclarationsHaveDocumentation": false
# }
# }
# 使用方式:
# 編輯器保存時格式化
# pre-commit 鈎子
# CI 檢查格式
# 與 SwiftLint 區別:
# format 管格式
# lint 管風格規則
# 兩者可配合

xcodebuild

Xcode 工程命令行構建。導出與歸檔。CI 使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 列出工程/工作區:
# xcodebuild -list
# 構建:
# xcodebuild -project App.xcodeproj \
# -scheme App -configuration Debug build
# 工作區構建:
# xcodebuild -workspace App.xcworkspace \
# -scheme App build
# 真機 SDK:
# -sdk iphoneos
# 模擬器:
# -destination 'platform=iOS Simulator,name=iPhone 15'
# 測試:
# xcodebuild test -scheme App
# 歸檔:
# xcodebuild archive -scheme App \
# -archivePath build/App.xcarchive
# 導出 IPA:
# xcodebuild -exportArchive \
# -exportOptionsPlist ExportOptions.plist
# CI 常用:
# 輸出結果用 xcresulttool 分析

發佈流程

版本控制、構建發佈、分發。發佈檢查清單。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 版本號規範:
# MAJOR.MINOR.PATCH
# 1.0.0 初始
# 2.1.0 新功能
# 2.1.3 修復
# Git 打標籤:
# git tag v2.1.0
# git push --tags
# 發佈流程:
# 1. 版本號 bump
# 2. CHANGELOG 更新
# 3. 測試全部通過
# 4. 構建發佈版
# 5. 生成歸檔
# 6. 簽名與公證
# 7. 上傳分發
# 命令行工具分發:
# 靜態編譯
# tar 打包
# Homebrew formula
# 檢查清單:
# 測試覆蓋
# 文檔更新
# 依賴鎖定
# 產物驗證
關於本速查

本頁是 Swift 5.9 的自包含速查手冊,覆蓋語言核心與最常用標準庫在真實項目中約 80% 的常見用法。內容偏向現代慣用法:值類型(struct/enum)、協議導向設計、async/await 結構化併發、if let 模式匹配、以及 Codable 序列化。權威參考見 Swift 官方語言指南與 API 設計規範。 19 個章節各自聚焦一個主題——從第一個程序到可選值、協議、併發與常見誤區。每節拆成 8 個帶示例的小節(每個 5–20 行),共約 150 個主題。代碼片段刻意短小、自解釋。 所有處理都在瀏覽器中完成——無上傳、無追蹤。本頁是 GuruToolkit 免費開發者工具集的一部分;代碼片段可自由使用,無任何擔保。

版本 2.1.0