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

相关推荐
崽崽长肉肉12 小时前
swift中的知识总结(一)
ios·swift
Yakamoz13 小时前
Swift Array的写时复制
swift
汉秋16 小时前
SwiftUI 中的 compositingGroup():真正含义与渲染原理
swiftui·swift
汉秋17 小时前
SwiftUI 中的 @ViewBuilder 全面解析
swiftui·swift
胖虎11 天前
SwiftUI 页面作为一级页面数据被重置问题分析
ios·swiftui·swift·state·observedobject·stateobject·swiftui页面生命周期
健了个平_241 天前
【iOS】如何在 iOS 26 的UITabBarController中使用自定义TabBar
ios·swift·wwdc
1024小神2 天前
xcode 配置了AppIcon 但是不显示icon图标
ios·swiftui·swift
奶糖 肥晨2 天前
架构深度解析|基于亚马逊云科技与Swift Alliance Cloud构建高可用金融报文交换架构
科技·架构·swift
Swift社区3 天前
用 Task Local Values 构建 Swift 里的依赖容器:一种更轻量的依赖注入思路
开发语言·ios·swift
TouchWorld3 天前
iOS逆向-哔哩哔哩增加3倍速播放(4)- 竖屏视频·全屏播放场景
ios·swift