Skip to main content

Swift 執行緒與並發程式設計

📋 目錄

  1. 執行緒基礎概念
  2. UI 更新原則
  3. 實務應用方法
  4. Swift Concurrency 關鍵字
  5. 最佳實踐

🔹 執行緒基礎概念

Main Thread (主執行緒)

  • 職責:負責 UI 操作(顯示畫面、處理觸控事件)
  • 規則:任何 UI 更新 必須 在主執行緒上執行
  • 注意:避免在主執行緒執行耗時工作,會導致 App 卡住

Background Threads (背景執行緒)

  • 用途:處理耗時工作(網路請求、檔案讀寫、圖片處理)
  • 流程:背景處理完成 → 切回主執行緒 → 更新 UI

🔹 UI 更新原則

⚠️ 重要:UI 更新只能在主執行緒 (Main Thread) 上進行

原因:UIKit / SwiftUI 不是 Thread-safe,在背景執行緒直接修改 UI 會導致錯誤或閃退。

🔹 實務應用方法

方法一:使用 DispatchQueue

// 在背景執行緒處理耗時工作
DispatchQueue.global().async {
let data = downloadData() // 模擬耗時工作

// 回到主執行緒更新 UI
DispatchQueue.main.async {
self.label.text = "下載完成"
}
}

方法二:搭配 async/await

func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "資料下載完成"
}

func loadData() {
Task {
let result = await fetchData()

// 在主執行緒更新 UI
await MainActor.run {
self.label.text = result
}
}
}

方法三:使用 @MainActor

@MainActor
func updateUI(text: String) {
self.label.text = text
}

// 或者標註整個 class
@MainActor
class ViewController: UIViewController {
// 所有方法都會在主執行緒執行
}

🔹 Swift Concurrency 關鍵字

1. async/await

Swift 5.5 引入的並發模型核心語法:

  • async:標記非同步函式
  • await:等待非同步函式完成,不阻塞執行緒
func fetchData() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000)
return "資料下載完成"
}

func load() async {
let result = await fetchData()
print(result) // 資料下載完成
}

2. @MainActor

確保程式在主執行緒執行,常用於 UI 更新:

@MainActor
func updateUI() {
// 一定會在主執行緒上執行
print("更新 UI")
}

// 搭配 async 使用
func loadData() async {
let result = await fetchData()
await MainActor.run {
print("更新畫面: \(result)")
}
}

3. @preconcurrency

用於與舊程式碼相容性:

@preconcurrency protocol MyLegacyProtocol {
func doSomething()
}

用途:讓舊的 API 可以在新的 Concurrency 環境下被接受,主要是過渡性工具。

🔹 最佳實踐

執行緒使用原則

  1. 主執行緒:專門負責 UI 更新和事件處理
  2. 背景執行緒:處理耗時工作
  3. 切換規則:背景工作完成 → 主執行緒更新畫面

推薦做法

  • 新專案優先使用 async/await + @MainActor
  • 舊專案可繼續使用 DispatchQueue
  • 避免在主執行緒執行耗時操作
  • 使用 @preconcurrency 處理相容性問題

常見錯誤

  • ❌ 在背景執行緒直接更新 UI
  • ❌ 在主執行緒執行網路請求
  • ❌ 忘記切回主執行緒更新畫面

📊 執行緒流程圖

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│ 使用者操作   │───▶│ 主執行緒   │───▶│ UI 更新 │
│ 點擊按鈕 │ │ (處理事件)   │ │ *顯示結果* │
└─────────────────┘ └──────────────────┘ └─────────────────┘


┌──────────────────┐
│ 背景執行緒    │
│ 下載資料     │
└──────────────────┘


┌──────────────────┐
│ 切回主執行緒 │
│ 更新 UI │
└──────────────────┘