假设您有一个在全局上下文中异步执行的方法.根据需要更新UI的执行情况.

private func fetchUser() async {
    do {
        let user = try await authService.fetchCurrentUser()
        view.setUser(user)
    } catch {
        if let error = error {
            view.showError(message: error.message)
        }
    }
}

切换到主线程的正确位置在哪里?

  1. @MainActor指定给fetchUser()方法:
@MainActor
private func fetchUser() async { 
    ...
}
  1. @MainActor指定给setUser(_ user: User)showError(message: String)视图的方法:
class SomePresenter {

    private func fetchUser() async {
        do {
            let user = try await authService.fetchCurrentUser()
            await view.setUser(user)
        } catch {
            if let error = error {
                await view.showError(message: error.message)
            }
        }
    }

}

class SomeViewController: UIViewController {

    @MainActor
    func setUser(_ user: User) {
        ...
    }

    @MainActor
    func showError(message: String) {
        ...
    }

}
  1. 不要指定@MainActor.将await MainActor.runTask@MainActor一起用于在主线程上运行setUser(_ user: User)showError(message: String)(如DispatchQueue.main.async):
private func fetchUser() async {
    do {
        let user = try await authService.fetchCurrentUser()
        
        await MainActor.run {
            view.setUser(user)
        }
    } catch {
        if let error = error {
            await MainActor.run {
                view.showError(message: error.message)
            }
        }
    }
}

推荐答案

选项2是合乎逻辑的,因为您允许必须在主队列上运行的函数声明自己.如果您错误地调用了它们,编译器会发出警告.更简单的是,您可以将具有这些函数的类本身声明为@MainActor,然后不必声明单个函数.E、 例如,由于设计良好的视图或视图控制器仅限于视图相关的代码,所以将整个类声明为@MainActor并使用它来完成是安全的.

选项3(代替选项2)很脆弱,要求应用程序开发人员必须记住在主参与者上手动将其run个.如果未正确执行操作,则会丢失编译时警告.编译时警告总是好的.但是WWDC 2021视频Swift concurrency: Update a sample app指出,即使您采用选项2,如果您需要调用一系列MainActor个方法,并且您可能不想产生一个接一个调用的开销,而是将一组主要参与者函数包装在一个MainActor.run块中,那么您仍然可以使用MainActor.run.(但您仍可以考虑将此与选项2结合使用,而不是替代选项2.)

抽象地说,选项1可以说有点过于苛刻,它指定的函数不一定要在主要参与者身上运行才能这样做.您应该只在明确需要/期望的地方使用主参与者.话虽如此,在实践中,我发现让演示者(或控制器或视图模型或您采用的任何模式)在主要参与者身上运行通常也很有用.例如,如果您有从演示者获取模型数据的同步UITableViewDataSourceUICollectionViewDataSource方法,则尤其如此.如果相关演示者使用不同的参与者,则无法始终同步返回到数据源.因此,您也可以在主参与者上运行presenter方法.同样,最好结合选项2来考虑,而不是替代选项2.

因此,简言之,选项2是谨慎的,但通常与选项1和3结合在一起.必须在主参与者上运行的 routine 应该被指定为这样的 routine ,而不是将此负担强加给调用者.


前面提到的Swift concurrency: Update a sample app条包含了许多实际考虑因素,如果您还没有看到,那么值得一看.

Ios相关问答推荐

SwiftUI 故障/错误 - 在 LazyVStack 和 ScrollView 中使用 AVPlayer 时状态栏不显示

UIView 阴影不适用于自定义扩展

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

SwiftUI 图表 BarMark 将注释对齐到图表顶部

在 SwiftUI 中,绑定应该放在 ViewModel 中吗?

为什么@FetchRequest 在删除时不更新和重绘视图?

Flutter 构建 ios 失败:运行 pod 安装时出错

发生异常:需要 issuerId

如何解码没有名称的 JSON 数组?

为 ColorPicker 创建一个名为 Color 的计算变量的绑定

添加到单个自定义视图时,Swiftui 项目会在所有视图中重复

UIViewRepresentable - MKMapView 没有得到更新

在 UICollectionView 上动态设置布局导致莫名其妙的 contentOffset 变化

如何阻止图像在 UIImageView 中拉伸?

在 Swift 中按下返回键时在文本字段之间切换

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

界面生成器中 UIView 的边框 colored颜色 不起作用?

判断可选数组是否为空

iOS 7 圆形框架按钮

iOS应用程序每隔一次启动就会崩溃,找不到错误