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主线程和主队列的区别

相关推荐
茶底世界之下18 小时前
Metal 冷启动并发下,Pipeline Cache 为什么必须合并在途编译
ios·swift
黑科技iOS上架2 天前
双引擎解决oc/swift开发引用加固难题
开发语言·ios·swift·审核·ios混淆·执行程序差异化
传奇开心果编程3 天前
【SwiftUI提高练中学】第13课 实时活动与灵动岛:ActivityKit 实战
学习·ui·ios·swiftui·swift
AC赳赳老秦4 天前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw
传奇开心果编程4 天前
【SwiftUI入门练中学】第17课 通知与后台任务
学习·ui·ios·swiftui·swift
逐流人5 天前
Ceph对象存储与文件系统存储管理:从RADOS网关、S3与Swift到CephFS快照同步
linux·运维·ceph·云原生·云计算·swift·rados
传奇开心果编程6 天前
【SwiftUI入门练中学】第7课 动画与手势:让界面更灵动
学习·macos·ui·ios·swiftui·swift
传奇开心果编程6 天前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift