iOS 队列与线程

在 Android 开发中我们所有 UI 操作只允许主线程和主队列操作(一些检查没开启不做讨论). 在学习iOS我一直也这样认为.

直到我看到下面的例子:

swift 复制代码
import SwiftUI
func helloFun() async{
    print("hello 3 ")
}
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
        }
        .padding().task {
            print("hello 1")
            await Task.detached {
               print("hello 2")
               await helloFun()
            }
        }
    }
}

#Preview {
    ContentView()
}

正常情况预期情况如下:

位置 线程 队列
hello1 主线程 主队列
hello2 子线程 全局队列
hello3 主线程 主队列

我将断点hello1,hello2,hello3 时使用 lldb 查看线程信息:

arduino 复制代码
 (lldb) thread list
Process 4136 stopped
* thread #8: tid = 0xf00000002, 0x00000001049422c8 LearnConcurrency.debug.dylib`closure #2 in ContentView.body.getter() at ContentView.swift:29:19, name = 'Task 2', queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  
"hello 1" 0x0000000104a1e354



  (lldb) thread list
Process 4136 stopped
 
* thread #13: tid = 0xf00000003, 0x0000000104a1e51c LearnConcurrency.debug.dylib`closure #1 in closure #2 in ContentView.body.getter() at ContentView.swift:31:22, name = 'Task 3', queue = 'com.apple.root.default-qos.cooperative', stop reason = breakpoint 3.1
 
"hello 2" 0x0000000104a1e51c



  (lldb) thread list
Process 4136 stopped
* thread #13: tid = 0xf00000003, 0x0000000104a1d6ac LearnConcurrency.debug.dylib`helloFun() at ContentView.swift:14:11, name = 'Task 3', queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
 

"hello 3" 代码位于 0x104a1d6ac

输出总结:

位置 tid 队列
hello1 0xf00000002 com.apple.main-thread
hello2 0xf00000003 com.apple.root.default-qos.cooperative
hello3 0xf00000003 com.apple.main-thread

初学 iOS 的小白愣了一会.反复查看 hello 1 汇编代码 PC 寄存地址确认没有错误.

hello1 和 hello3 都可以视为在主线程执行,因为加入类似的如下代码会卡死界面:

swift 复制代码
func helloFun() async{
    print("hello 3 ")
    //会卡死界面
    while true{
    }
}

我一直以为主线程和主队列是 1 对 1 的关系,但是看到这个信息感觉是所有主队列任务可运行在任何线程.但是主队列任务是串行执行.

hello1 和 hello3 的 tid 截然不同的,这和 Android 成为了巨大反差. 但是在 hello2添加类似的代码不会.

swift 复制代码
task {
    print("hello 1")
    await Task.detached {
       print("hello 2")
       //不会卡死 UI
        while true{

        }
       await helloFun()
    }
}

所以粗浅的得到下面结论:

  1. func helloFun()会默认继承 MainActor,所以定会在主队列执行.
  2. MainActor不保证在主线程执行,但是保证在主队列.
  3. 为了屏蔽线程差异,swift 在所有 async 函数中全部屏蔽Thread.current调用.
  4. UI 元素不一定要在主线程,但是一定要在主队列
  5. 主队列执行可能在子线程,但是会阻塞后续队列执行直到完成以避免主线程的竞争问题
  6. Thread.current.isMainThread 可能被底层封装了.即便当前是一个个其他线程.或者他以主队列作为主线程标准

一些技巧

  1. 虽然swift6 不允许在 async 编写 Thread.current 但是可以在 LLDB 中输入 po Thread.current去调试查看

  2. 使用 lldb 的 thread list可以查看所有线程的信息.

  3. 使用 lldb 的 thread info 可以查看当前线程

参考链接

iOS主线程和主队列的区别

相关推荐
HarderCoder3 小时前
【Swift Concurrency】深入理解 `async let` 与 `TaskGroup`:并发任务的生命周期与错误传播机制
swift
HarderCoder3 小时前
深入理解 Swift Concurrency:从 async/await 到 Actor 与线程池的完整运行机制
swift
HarderCoder3 小时前
Swift 结构化并发 6 条铁律 —— 一张图 + 一套模板,让 `async let` / `TaskGroup` / `Task {}` 不再踩坑
swift
M-finder1 天前
Mac菜单栏综合工具FancyTool更新啦
mac·swift
HarderCoder3 天前
在同步代码里调用 async/await:Task 就是你的“任意门”
swift
HarderCoder3 天前
Swift 三目运算符指南:写法、场景与避坑
swift
YungFan3 天前
iOS26适配指南之UISlider
ios·swift
HarderCoder3 天前
一篇读懂 Swift 不透明类型:让带 associatedtype 的协议也能当返回值
swift
HarderCoder3 天前
`@dynamicCallable`:把 Swift 对象当函数喊
swift
HarderCoder4 天前
调试 Swift 并发:我到底在哪个 Actor?
swift