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

相关推荐
看谷秀11 小时前
swift Part 1
swift
Daniel_Coder14 小时前
iOS Widget 开发-14:iOS 18 控制中心组件开发
ios·swift·widget·activitykit·widgetkit·控制中心组件
sakiko_15 小时前
Swift学习笔记29-数据库SQlite
数据库·学习·sqlite·swift
2501_9159214317 小时前
使用Swift和Xcode创建简单iOS应用完整教程
ide·vscode·ios·个人开发·xcode·swift·敏捷流程
Daniel_Coder17 小时前
iOS Widget 开发-13:Live Activity 实战详解
ios·swift·widget·widgetkit·controls·live activity
sakiko_1 天前
Swift学习笔记28-缓存
笔记·学习·swift
hhb_6182 天前
Swift技术难点梳理与实战案例解析
开发语言·ios·swift
Daniel_Coder2 天前
iOS Widget 开发-12:Widget 深度链接与导航
ios·swiftui·swift·widget·intents
Daniel_Coder2 天前
iOS Widget 开发-11:Widget 交互按钮实战(iOS 17+ App Intents)
ios·swiftui·swift·widget·link·appintents
东坡肘子2 天前
消失的 WWDC 愿望单 -- 肘子的 Swift 周报 #136
人工智能·swiftui·swift