在指定了@MainActor的Swift方法中,该方法调用withCheckedContinuation并进行其他异步调用,似乎在@MainActor线程上调用了withCheckedContinuation,但其他异步调用并非如此.

这是巧合吗?

可以将以下代码放入XCode为您创建的默认iOS App的ContentView.swft中:

import SwiftUI

func printCurrentThread(_ label: String) {
    print("\(label) called on \(Thread.current)")
}

class SomeNonThreadSafeLibrary {
    func getFromServer(msg: String, completionHandler: @escaping (String) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            completionHandler(msg)
        }
    }
}

class AppController: ObservableObject {
    @Published var something1: String = "not set"
    @Published var something2: String = "not set"
    var someNonThreadSafeLibrary: SomeNonThreadSafeLibrary? = SomeNonThreadSafeLibrary()

    func someOtherAsyncFunction(calledFrom: String) async {
        printCurrentThread("\(calledFrom) -> someOtherAsyncFunction")
    }

    func getSomethingAsynchronously() async -> String {
        printCurrentThread("getSomethingAsynchronously")
        await someOtherAsyncFunction(calledFrom: "getSomethingAsynchronously")
        return await withCheckedContinuation { continuation in
            printCurrentThread("getSomethingAsynchronously -> withCheckedContinuation")
            do {
                try Task.checkCancellation()
                self.someNonThreadSafeLibrary?.getFromServer(msg: "getSomethingAsynchronously") { result in
                    printCurrentThread("getSomethingAsynchronously -> getFromServer closure")
                    continuation.resume(returning: result)
                }
            }
            catch {}
        }
    }

    @MainActor
    func getSomethingAsynchronouslyWithMainActor() async -> String {
        printCurrentThread("getSomethingAsynchronouslyWithMainActor")
        await someOtherAsyncFunction(calledFrom: "getSomethingAsynchronouslyWithMainActor")
        return await withCheckedContinuation { continuation in
            printCurrentThread("getSomethingAsynchronouslyWithMainActor -> withCheckedContinuation")
            do {
                try Task.checkCancellation()
                self.someNonThreadSafeLibrary?.getFromServer(msg: "getSomethingAsynchronouslyWithMainActorWithMainActor") { result in
                    printCurrentThread("getSomethingAsynchronouslyWithMainActor -> getFromServer closure")
                    continuation.resume(returning: result)
                }
            }
            catch {}
        }
    }

    @MainActor
    func getValueForSomething() async {
        something1 = await getSomethingAsynchronously()
    }

    @MainActor
    func getValueForSomethingWithMainActor() async {
        something2 = await getSomethingAsynchronouslyWithMainActor()
    }

}

struct ContentView: View {
    @StateObject private var appController = AppController()
    var body: some View {
        VStack {
            Text("something1 is \(appController.something1)")
            Text("something2 is \(appController.something2)")
        }
        .task {
            await appController.getValueForSomething()
        }
        .task {
            await appController.getValueForSomethingWithMainActor()
        }
        .padding()
    }
}

代码的输出如下所示...

在线程1上调用带@MainActor的函数,在任意线程上调用不带@MainActor的函数,正如预期的那样:

getSomethingAsynchronouslyWithMainActor called on <_NSMainThread: 0x600001910400>{number = 1, name = main}
getSomethingAsynchronously called on <NSThread: 0x60000195c300>{number = 7, name = (null)}

然后,从这些方法中的每一个调用不做任何事情的函数someOtherAsyncFunction,并且在两种情况下(即,在调用函数上具有或不具有@MainActor),它在任意线程上被调用.这表明函数上的@MainActor不会被其内部调用的函数继承:

getSomethingAsynchronously -> someOtherAsyncFunction called on <NSThread: 0x60000195c300>{number = 7, name = (null)}
getSomethingAsynchronouslyWithMainActor -> someOtherAsyncFunction called on <NSThread: 0x6000019582c0>{number = 6, name = (null)}

然而,在闭包为withCheckedContinuation的特定情况下,当@MainActor在调用函数上时,它似乎确实在线程1上运行:

 getSomethingAsynchronously -> withCheckedContinuation called on <NSThread: 0x60000195c300>{number = 7, name = (null)}
 getSomethingAsynchronouslyWithMainActor -> withCheckedContinuation called on <_NSMainThread: 0x600001910400>{number = 1, name = main}

所以...withCheckedContinuation似乎通过它的调用函数在@MainActor上运行,这只是一个巧合,还是故意的?为什么只有withCheckedContinuation,而不是someOtherAsyncFunction

如果它是设计出来的,我从哪里可以获得关于它在哪里指定的更多信息?

推荐答案

someOtherAsyncFunction是一个async方法,它不是孤立于任何特定参与者的,因此它可以自由地在它想要的任何线程(假设是协作线程池中的一个线程)上运行它.但是withCheckContinuation的闭包参数不是async闭包,而是一个简单的同步闭包,所以它在启动它的任何线程上运行似乎是完全合乎逻辑的(尽管我在文档中没有看到任何关于这一点的正式保证).

尽管如此,值得注意的是,如果您希望在特定参与者上运行特定的异步函数,与GCD不同,我们从不根据调用者的上下文对调用位置进行假设.相反,我们只需要隔离我们正在调用的适当参与者的方法,或者使用大约global actor的名称(例如@MainActor),或者通过在actor中定义async方法,我们就完成了.

相比之下,在GCD中,调用者通常会将一些代码分派到某个特定队列.随着我们的项目变得更加复杂,我们有时会引入一些带有dispatchPreconditionassert个测试的防御性编程技术,以确保某个方法确实在我们认为的队列上运行.

但SWIFT并发性消除了我们代码中的所有这些假设(以及可能对这些假设的测试).我们只是将特定的方法隔离到特定的参与者,而SWIFT并发性从那里开始处理它.

有关这方面的实际例子,请参阅WWDC 2021视频27 minutes in94/" rel="nofollow noreferrer">Swift concurrency: Update a sample app,特别是约27 minutes in.

此外,在您的问题中,您正在判断某个任务或延续运行的线程.但是,SWIFT并发性可能会打破我们传统的线程期望(在future 的编译器版本中,您甚至可能根本不能从异步上下文中使用Thread.current).但如果你对其中的一些细节感兴趣,请参阅WWDC 2021视频Swift concurrency: Behind the scenes.它与您的问题没有直接关系,但它确实提供了对SWIFT并发线程模型的有趣一瞥.

Ios相关问答推荐

使用UIGraphicsBeginIMAext创建新图像会扭曲图像 colored颜色

Xcode版本15.2上未显示iOS 16.4的模拟器

由于代码签名时出错,无法在iOS设备上发布iOS Maui Build

在SwiftUI中为圆角矩形创建均匀分布的点轮廓

Xamarin Forms CustomPin iOS Render

iOS 17-在Xcode 15中添加导航控制器时,导航栏不会立即显示

在 iOS 上以后台模式安排任务(屏幕时间 API)

如何将 AppIntent 添加到应用程序目标并在交互式小部件中使用它

使用 SwiftUI 显示多个 VNRecognizedObjectObservation 边界框时偏移量错误

在左侧显示多行值时会出现 SwiftUI 错误,这会 destruct 堆栈中右侧的对齐方式

在 Swift 中从 Objective C 集合调用块

iOS 是否支持使用独立 .xib 文件创建的嵌套自定义子视图?

.onAppear() 函数不适用于 NavigationLink

通过 tuist/XcodeProj 以编程方式将文件添加到本机目标

UILabel - 字符串作为文本和链接

如何更新推送通知服务证书

如何使用 Swift 从assets资源 中加载特定图像

在 React Native 中保存敏感数据

如何更改uitableview删除按钮文本

判断从 JSON 字符串返回的 Objective-C 中的空值