惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
E
Exploit-DB.com RSS Feed
T
Tenable Blog
P
Privacy International News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
H
Hacker News: Front Page
S
Securelist
Recent Commits to openclaw:main
Recent Commits to openclaw:main
A
Arctic Wolf
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Cloudbric
Cloudbric
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
I
Intezer
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 叶小钗
The Cloudflare Blog
L
LINUX DO - 热门话题
Last Week in AI
Last Week in AI
V
V2EX
L
LINUX DO - 最新话题
AWS News Blog
AWS News Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Hacker News
The Hacker News
W
WeLiveSecurity
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
PCI Perspectives
PCI Perspectives
SecWiki News
SecWiki News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
WordPress大学
WordPress大学
罗磊的独立博客
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
D
Docker
O
OpenAI News
Martin Fowler
Martin Fowler
N
News and Events Feed by Topic
S
Security @ Cisco Blogs
G
Google Developers Blog
H
Heimdal Security Blog
T
Troy Hunt's Blog
Vercel News
Vercel News
Recent Announcements
Recent Announcements
爱范儿
爱范儿
AI
AI

掘金

Win 安装Claude Code FastAPI 的 CORSMiddleware 跨域中间件 Java 自研 ReAct Agent 半年后,我用 LangGraph 验证了这些设计取舍 🚀AI编程工作流终极形态:GitNexus!零Token消耗实现代码知识图谱化!让Claude Code和Codex拥有上帝视角彻底告别盲目改代码,复杂项目重 LeetCode 72. 编辑距离:动态规划经典题解 被The Graph的GraphQL查询坑了三天,我用一个真实DeFi项目把链上数据索引彻底搞懂了 (AI) 编写简单 AI 助手 (ds-agent) 别再让 pnpm 跟着 nvm 跑了!独立安装终极指南 Claude Code 为什么这么顺?Anthropic 最新复盘:真正撑住它的不是模型,而是缓存 从 /simplify 指令深挖 Claude Code 多 Agent 协同机制 Function-Calling与工具使用 新手上路(六):Claude code装上ECC全家桶:38 个子代理、156 个技能、生产级 Hooks 与 Rules 体系 我在 Claude、Kimi、opencode 三个 AI 之间搭了一条自动协作管道 【技能篇】OpenClaw Skill 详解:给 AI 装上"专业外挂" wagmi v2 多链钱包切换:一个 Uniswap 仿盘项目让我踩了三天坑 两周浅学 RAG 我把 Python re 模块比喻成摸金手套 新手上路(三):Claude Code Skills 装了一堆没用?20+ 个 Skill 横向对比 + 三套组合方案,按需抄 K2.6、DeepSeek V4、GPT-5.5 都来了,组合拳打起来 Claude Code 进阶之路:从记忆系统到子代理编排 [java] 编译之后的记录类(Record Classes)长什么样子(上) 国产大模型能力大比拼,社区有话说 我研读了 500 个 Spring Boot 生产级代码库,90% 都犯了这 7 个致命错误 JAVA重点难点 转发-中央网信办部署开展“清朗·整治AI应用乱象”专项行动 合同同步逻辑 【合并已排序数组的三种实现策略,哪一种更可取?】 30天减20斤挑战:少一斤发100红包(2) 我竟然被JavaScript的隐式类型转换坑了三天! 二十五.Electron 初体验与进阶 本地到生产,解决 AI 全栈最后一公里——构建&部署&运维 程序员创业半年:顺的事、不顺的事,和我一直没想清楚的事 UI组件库elementplus 像使用 Redis 一样操作 LocalStorage 向量检索的流程是怎样的?Embedding 和 Rerank 各自的作用? LangChain DeepAgents 速通指南(七)—— DeepAgents使用Agent Skill 为什么越来越多的大厂抛弃MCP,转向CLI? 【节点】[SquareRoot节点]原理解析与实际应用 juejin.cn juejin.cn 从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台越来越多工业用户开始意识到一个问题:**数据是存下来了, - 掘金 放弃 Claude 订阅?我用 8 年前的服务器,强跑 Google 最强开源模型 Gemma 4 真实测评! Python开发者狂喜!200+课时FastAPI全栈实战合集,10大模块持续更新中🔥 从 Claw-Code 看 AI 驱动的大型项目开发:2 人 + 10 个自治 Agent 如何产出 48K 行 Rust 代码 秒级创建实例,火山引擎 Milvus Serverless 让 AI Agent 开发更快更省火山引擎MilvusSer MediaPlayer 播放器架构:NuPlayer 的 Source/Decoder/Renderer 三驾马车 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn OrbStack:一键将你的 Mac 变为本地服务器 NginxPulse:Nginx日志监控革命!实时洞察Web流量与安全态势的智能利器引言:当Nginx日志成为运维的“数 - 掘金 juejin.cn 大V说’AI替代不了你’,但现实是——用AI的人正在替代你2026年是AI落地的元年,自从Claude Code爆火之后 - 掘金 juejin.cn 你以为是技术问题,其实是流程问题:工程效率的真相引言 在软件工程领域,效率问题始终是团队管理者和工程师们关注的焦点。当项 - 掘金 大模型工程三驾马车:Prompt Engineering、Context Engineering 与 Harness Engineering 深度解析 juejin.cn 4.响应式系统基础:从发布订阅模式的角度理解 Vue3 的数据响应式原理本文从发布订阅模式的核心思想出发,深入剖析了 V - 掘金 慌了!Android 17 取消图标文字,你的 App 可能要找不到了用户终于可以隐藏桌面图标下面的文字了。 这个功能在 juejin.cn 我用 AI 搓了一个"比谁更持久"的微信小游戏,AI实现只用了一天,微信审核却用了一个月!!!起因:一个沙雕想法的诞生 - 掘金 juejin.cn 第12章 工具(Tools)与函数调用(LangChain实战)在前几章中,我们搭建的RAG系统、对话链,核心能力局限 - 掘金 juejin.cn CmComposeUI —— 基于 Kotlin Multiplatform Compose 的 UI 组件库 Android 开发的 AI coding 与 AI debugging在目前整个行业都在大规模使用 AI coding juejin.cn juejin.cn juejin.cn juejin.cn 一文搞懂Harness Engineering与Meta-Harness 越用越强不是广告语:拆解 Hermes Agent 的三层学习机制 P2G-Python字符串方法完全指南-split、join、strip、replace的Python编程利器 AI 周刊【2026.04.06-04.12】:Anthropic 藏起最强模型、AI 社会矛盾激化、"欢乐马"登顶 从 AI Skills 学实战技能(六):让 AI 帮你总结网页、PDF、视频 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 详解 karpathy 的 microgpt:实现一个浏览器运行的 gpt 不用 Tailscale:3 步把 Mac mini 通过 FRP 暴露到公网(稳定开机自启) P2B-Python可迭代对象完全指南-从列表到生成器的Python编程利器 手把手带你部署本地模型,让你Token自由(小白专属) juejin.cn 10分钟掌握 JSON-RPC 协议,面试加分、设计不踩坑 ReAct:让大模型学会边想边做 聊聊AI的发展史,AI的爆发并不是偶然 Python的列表推导式里藏了个坑,差点让我加班到凌晨 重排、重绘与合成——浏览器渲染性能的底层逻辑 podman与docker的区别和生产环境最佳实践 juejin.cn ConcurrentHashMap线程安全实现原理全解析 juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn juejin.cn OpenAI Codex深度解析:终端里的AI代码特工,一个指令重构整个项目 UE5.6 Cesium 插件编译踩坑记录(UE 5.6 + MSVC 14.38 + CMake 3.31)
SwiftUI 多线程与并发编程深度总结
90后晨仔 · 2026-05-18 · via 掘金

在 iOS 开发中,多线程与并发编程是构建流畅、响应式应用的核心能力。从早期的 GCD 和 OperationQueue,到 Swift 5.5 引入的 async/await 结构化并发,再到 Swift 5.9 完善的 Actor 模型,Apple 为我们提供了越来越安全、易用的并发工具。在 SwiftUI 中,声明式 UI 与响应式数据流让线程管理的重要性进一步提升:后台获取数据,主线程刷新界面,取消多余任务,保护共享状态——这一切都有对应的最佳实践。

本文将系统梳理 SwiftUI 开发中涉及的多线程与并发技术栈,从基础概念到实战案例,助你彻底掌握现代 iOS 并发编程。

1. 基础概念:线程、并发与并行

在进入框架之前,先理清几个容易被混淆的核心概念。

  • 进程(Process) 是操作系统分配资源的基本单位,一个应用通常对应一个进程。
  • 线程(Thread) 是 CPU 调度的最小单位,一个进程内可以包含多个线程。
  • 主线程(Main Thread) 是专门处理 UI 事件的特殊线程,所有 UIKit 和 SwiftUI 的视图更新必须发生在此线程上。
  • 后台线程(Background Thread) 用于执行耗时操作,避免阻塞主线程导致界面卡顿。

并发 vs 并行

  • 并发(Concurrency):多个任务在宏观上同时进行,但在单核 CPU 上实际是交替执行。
  • 并行(Parallelism):多个任务真正在不同的 CPU 核心上同时执行。

Swift 的多线程技术正是围绕着如何高效、安全地实现并发与并行而设计的。

2. GCD(Grand Central Dispatch)

GCD 是 Apple 提供的一套基于 C 语言的并发编程框架,它通过队列(Queue)抽象线程管理,开发者只需将任务提交到队列,系统自动分配线程执行。

2.1 队列类型

队列类型创建方式执行方式
主队列DispatchQueue.main串行,在主线程执行,用于 UI 更新
全局并发队列DispatchQueue.global(qos:)并发,系统共享,可指定 QoS 优先级
自定义串行队列DispatchQueue(label:)串行,任务按顺序执行
自定义并发队列DispatchQueue(label:attributes: .concurrent)并发,任务可同时执行

QoS(Quality of Service)优先级 决定了系统为任务分配资源的倾向:

QoS 类用途对能耗的影响
.userInteractive动画、UI 响应高能耗
.userInitiated用户主动触发的即时操作(如按钮响应)中等偏高
.default默认优先级中等
.utility耗时但用户可感知的任务(下载、数据处理)中等偏低
.background用户无感知的后台维护任务低能耗

2.2 异步与同步

// 异步执行:不阻塞当前线程
DispatchQueue.global().async {
    let result = performHeavyWork()
    DispatchQueue.main.async {
        // 回到主线程更新 UI
        self.updateUI(with: result)
    }
}

// 同步执行:阻塞当前线程直到任务完成(谨慎使用,避免死锁)
DispatchQueue.global().sync {
    // 此处会等待闭包执行完毕
}

2.3 任务协调利器

DispatchGroup:等待一组任务全部完成。

let group = DispatchGroup()

for url in urls {
    group.enter()
    download(url) {
        group.leave()
    }
}

group.notify(queue: .main) {
    print("所有下载完成")
}

DispatchWorkItem:可取消的代码块。

let workItem = DispatchWorkItem {
    print("执行任务")
}
DispatchQueue.global().async(execute: workItem)
workItem.cancel() // 在任务开始前调用可取消执行

Barrier(栅栏):在并发队列中插入屏障,确保之前的任务全部完成后才执行屏障任务,且屏障期间队列不再调度其他任务。

let queue = DispatchQueue(label: "com.example", attributes: .concurrent)
queue.async { /* 读操作 1 */ }
queue.async { /* 读操作 2 */ }
queue.async(flags: .barrier) {
    // 写操作,独占队列
}
queue.async { /* 后续读操作 */ }

3. OperationQueue

OperationQueue 是对 GCD 的面向对象封装,提供了更丰富的功能,如依赖管理、取消、状态监听、最大并发数控制等。

3.1 基础用法

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
queue.qualityOfService = .userInitiated

let operation = BlockOperation {
    print("任务执行")
}
queue.addOperation(operation)

3.2 依赖关系

let op1 = BlockOperation { print("1") }
let op2 = BlockOperation { print("2") }
let op3 = BlockOperation { print("3") }

op3.addDependency(op1)
op3.addDependency(op2)

queue.addOperations([op1, op2, op3], waitUntilFinished: false)
// 输出顺序为 1、2 后再输出 3

3.3 自定义 Operation

继承 Operation 可实现可复用的并发任务,并精确控制执行与取消。

class DownloadOperation: Operation {
    private let url: URL
    private var task: URLSessionDataTask?

    init(url: URL) { self.url = url }

    override func main() {
        guard !isCancelled else { return }
        let semaphore = DispatchSemaphore(value: 0)
        task = URLSession.shared.dataTask(with: url) { data, _, error in
            defer { semaphore.signal() }
            if let data = data, !self.isCancelled {
                // 处理数据
            }
        }
        task?.resume()
        semaphore.wait()
    }

    override func cancel() {
        task?.cancel()
        super.cancel()
    }
}

4. async/await 结构化并发

Swift 5.5 引入的 async/await 让异步代码看起来像同步代码一样清晰,从根本上消灭了“回调地狱”。SwiftUI 也为之提供了 .task.refreshable 等原生支持。

4.1 基本语法

func fetchData() async throws -> Data {
    let (data, response) = try await URLSession.shared.data(from: url)
    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    return data
}

调用异步函数必须在 Task 内部或另一个异步上下文中。

Task {
    do {
        let data = try await fetchData()
        // 更新 UI(需要主线程)
    } catch {
        print(error)
    }
}

4.2 Task 与 TaskGroup

  • Task:创建一个独立的并发任务,有优先级和取消支持。
  • TaskGroup:动态创建多个子任务,并等待全部完成,结果按完成顺序收集。
let results = await withTaskGroup(of: String.self) { group in
    for i in 1...5 {
        group.addTask {
            try await Task.sleep(nanoseconds: UInt64(i) * 500_000_000)
            return "任务 \(i)"
        }
    }
    var collected = [String]()
    for await result in group {
        collected.append(result)
    }
    return collected
}

4.3 async let 并行绑定

当并行任务数量固定时,可以使用 async let 同时启动多个异步调用,并在需要结果时 await 它们。

async let user = fetchUser()
async let posts = fetchPosts()
async let comments = fetchComments()

let (userData, postsData, commentsData) = await (user, posts, comments)

4.4 结构化取消

任务会随着父任务的取消而自动取消。在长时间运行的任务内部,应定期检查 Task.isCancelled 或调用 try Task.checkCancellation()

for item in items {
    try Task.checkCancellation()
    process(item)
}

4.5 优先级与独立任务

子任务默认继承父任务优先级,也可通过 Task.detached 创建独立优先级的任务。

Task.detached(priority: .background) {
    await backgroundSync()
}

5. 线程安全与 Actor 模型

多个线程同时访问可变状态会导致数据竞争(Data Race),这是并发编程中最隐蔽且危险的问题。Swift 从语言层面提供了多种解决方案。

5.1 传统同步手段

  • NSLock / os_unfair_lock:手动加锁,容易出错。
  • 串行队列:将共享状态的读写操作限制在同一个队列上执行。
class SafeCounter {
    private var count = 0
    private let queue = DispatchQueue(label: "counter.queue")

    func increment() {
        queue.sync { count += 1 }
    }

    var value: Int {
        queue.sync { count }
    }
}

5.2 Actor(推荐方式)

Actor 是 Swift 5.5 引入的引用类型,它自动保护自身内部状态,外部只能通过异步方法访问,从根本上避免了数据竞争。

actor SafeCounter {
    private var count = 0

    func increment() { count += 1 }
    func decrement() { count -= 1 }
    var current: Int { count }
}

let counter = SafeCounter()
Task {
    await counter.increment()
    print(await counter.current)
}

Actor 的关键规则

  • 所有对 Actor 可变状态的访问都必须使用 await
  • Actor 内部的方法可以是同步的,因为它们已经处于隔离域内。
  • Actor 可以遵守 Sendable 协议,表示其状态可以安全跨并发域传递。

5.3 @MainActor

@MainActor 是一个全局 Actor,代表主线程。将类或方法标记为 @MainActor 后,所有属性和方法的访问都会自动在主线程执行,非常适合 SwiftUI 的 ViewModel。

@MainActor
class DataViewModel: ObservableObject {
    @Published var items: [Item] = []

    func load() async {
        let data = await fetchFromNetwork() // 后台
        items = data // 主线程更新
    }
}

5.4 Sendable 与数据安全

Swift 编译器通过 Sendable 协议标记可以在并发域间安全传递的类型。值类型(如 Int、String、struct)通常自动符合 Sendable;引用类型需手动遵循并保证内部线程安全。正确使用 Sendable 可避免许多隐藏的数据竞争。

6. 主线程与 UI 更新

所有 SwiftUI 的视图刷新和绑定属性(@State, @StateObject)的更改都必须在主线程进行。违反此规则通常会导致运行时警告或未定义行为。

6.1 回到主线程的三种方式

// 方式一:传统 GCD
DispatchQueue.main.async { self.viewModel.data = data }

// 方式二:使用 Task 闭包配合 @MainActor
Task { @MainActor in
    self.viewModel.data = data
}

// 方式三:使用 MainActor.run
await MainActor.run {
    self.viewModel.data = data
}

6.2 SwiftUI 生命周期中的并发

SwiftUI 提供了两个关键修饰符来集成异步任务:

  • .task:视图出现时启动异步任务,消失时自动取消,完美替代 onAppear 中的手动管理。
  • .refreshable:支持下拉刷新,自动管理加载状态。
struct ContentView: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        List(viewModel.items) { item in Text(item.name) }
            .task { await viewModel.load() }
            .refreshable { await viewModel.refresh() }
    }
}

7. 并发模式与常见陷阱

7.1 应该遵循的模式

  • 后台获取,主线程渲染:所有 I/O、网络、计算密集型工作放到后台。
  • 结构化并发:用 TaskGroup 或 async let 管理子任务,生命周期与父任务绑定。
  • 利用 Actor 封装状态:任何需要跨线程修改的数据,放进 Actor。
  • 及时取消:在 ViewModel 析构或视图消失时取消正在执行的任务。
  • 错误传播:使用 throws 明确错误路径,避免静默失败。

7.2 要避免的反模式

反模式后果改进方法
在主线程执行同步网络请求界面卡死使用 async/await 或后台队列
多个线程同时修改可变状态而不加锁数据竞争、崩溃使用 Actor 或队列同步
创建远多于 CPU 核心数的线程线程爆炸、性能下降使用 OperationQueue 限制并发数
嵌套回调(回调地狱)可读性差、难以取消使用 async/await
忘记取消后台任务内存泄漏、无效操作利用 .task 或存储 Task 并适时取消

8. 性能优化与调试

8.1 控制并发度

通过 OperationQueue.maxConcurrentOperationCountTaskGroup 的限流(需手动实现信号量)来避免同时启动过多网络请求。

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 6 // 根据设备性能调整

8.2 缓存与重复请求合并

利用 NSCache 或自定义缓存避免重复的网络请求,配合 Combine 的 share() 或者 async 版本的缓存管理器。

8.3 调试工具

  • Instruments - Threads:查看线程活动,发现线程爆炸或主线程阻塞。
  • Xcode 断点条件:设置 Thread.isMainThread 等条件精准定位。
  • Swift Concurrency 检查器:在 Product > Scheme 中启用 Thread Sanitizer 和 Actor 隔离检查。

9. 与 Combine 集成

Combine 和 Swift Concurrency 并非互斥关系。在已有 Combine 代码库中,可平滑过渡:

  • Publisher → AsyncSequence:通过 .values 将 Publisher 转为可迭代的异步序列。
  • Async → Publisher:使用 Future 包装 async 函数。
// Combine 转 async
func fetch() async throws -> Data {
    let publisher = URLSession.shared.dataTaskPublisher(for: url).map(\.data)
    for try await data in publisher.values {
        return data
    }
    throw URLError(.badServerResponse)
}

// async 转 Combine
func fetchPublisher() -> AnyPublisher<Data, Error> {
    Future { promise in
        Task {
            do {
                let data = try await fetch()
                promise(.success(data))
            } catch {
                promise(.failure(error))
            }
        }
    }.eraseToAnyPublisher()
}

10. 实战:一个完整的新闻列表

结合所述技术,构建一个具有下拉刷新、后台加载、线程安全的 SwiftUI 新闻列表。

@MainActor
class NewsViewModel: ObservableObject {
    @Published var articles: [NewsArticle] = []
    @Published var isLoading = false
    @Published var errorMessage: String?

    private let service = NewsService()
    private var loadingTask: Task<Void, Never>?

    func load() async {
        isLoading = true
        errorMessage = nil
        do {
            articles = try await service.fetchArticles()
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }

    func refresh() async {
        loadingTask?.cancel()
        loadingTask = Task { await load() }
    }
}

struct NewsListView: View {
    @StateObject private var viewModel = NewsViewModel()

    var body: some View {
        List(viewModel.articles) { article in
            Text(article.title)
        }
        .overlay {
            if viewModel.isLoading { ProgressView() }
        }
        .task { await viewModel.load() }
        .refreshable { await viewModel.refresh() }
    }
}

该示例严格遵循主线程更新 UI、后台获取数据,并通过 .taskTask 管理生命周期,避免内存泄漏。

11. 总结

SwiftUI 的多线程与并发编程已经从原始的 GCD 闭包进化到了以 Actor 和 async/await 为核心的结构化并发时代。理解线程职责分离、共享状态的保护、任务生命周期管理以及 Combine 的互操作,是写出高效、安全、可维护 iOS 应用的必备技能。

核心检查清单

  • ✅ 耗时操作在后台,UI 更新在主线程。
  • ✅ 使用 @MainActor 保护 ViewModel 的 UI 绑定属性。
  • ✅ 共享可变状态封装于 Actor。
  • ✅ 利用 .task.refreshable 管理异步生命周期。
  • ✅ 避免过度并发,适当限制 OperationQueue 的并发数。
  • ✅ 正确处理取消与错误,不留下僵尸任务。

掌握这些原则,你的 SwiftUI 应用将在任何设备上都保持丝滑流畅,并具备工业级的稳定性。