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 小时前
SwiftUI 状态管理极简之道:从“最小状态”到“状态树”
swift
Antonio9153 小时前
【Swift】 UIKit:UIGestureRecognizer和UIView Animation
开发语言·ios·swift
蒙小萌199313 小时前
Swift UIKit MVVM + RxSwift Development Rules
开发语言·prompt·swift·rxswift
Antonio91521 小时前
【Swift】Swift基础语法:函数、闭包、枚举、结构体、类与属性
开发语言·swift
Antonio9151 天前
【Swift】 Swift 基础语法:变量、类型、分支与循环
开发语言·swift
songgeb1 天前
[WWDC 21]Detect and diagnose memory issues 笔记
性能优化·swift
00后程序员张3 天前
Swift 应用加密工具的全面方案,从源码混淆到 IPA 成品加固的多层安全实践
安全·ios·小程序·uni-app·ssh·iphone·swift
非专业程序员3 天前
Swift 多线程读变量安全吗?
swiftui·swift
Sirius Wu3 天前
开源训练框架:MS-SWIFT详解
开发语言·人工智能·语言模型·开源·aigc·swift
从零开始学习人工智能3 天前
USDT区块链转账 vs SWIFT跨境转账:技术逻辑与场景博弈的深度拆解
开发语言·ssh·swift