我有以下视图模型:

@MainActor
final class ProfileViewModel: ObservableObject {
  let authService: AuthService
  
  @Published var userHandle: String = "Unknown"
  
  nonisolated init(authService: AuthService, appState: AppState) {
    self.authService = authService
    appState.$userData
      .receive(on: DispatchQueue.main)
      .map {
        $0.user?.displayName ?? "Unknown"
      }
      .assign(to: &$userHandle)
  }
}

这会导致错误:"主参与者隔离属性'$userHandle'cannot used 'inout'from a non—isolated context",位于". assign(to:)"行.

这在某种程度上是意料之中的.据我所知,Combine的动态线程切换并不满足Swift并发模型的编译时保证.

下面是让我困惑的地方.如果我从类中删除@ MainActor注释,并将其应用于userHandle属性,如下所示:

final class ProfileViewModel: ObservableObject {
  let authService: AuthService
  
  @MainActor @Published var userHandle: String = "Unknown"
  
  nonisolated init(authService: AuthService, appState: AppState) {
    self.authService = authService
    appState.$userData
      .receive(on: DispatchQueue.main)
      .map {
        $0.user?.displayName ?? "Unknown"
      }
      .assign(to: &$userHandle)
  }
}

代码编译时没有问题.

从逻辑上讲,这应该引发相同的错误,因为userHandle在两种情况下都是Main actor隔离的.我想知道这是不是编译器的怪癖,还是有一个我不明白的更深层次的语义差异.

推荐答案

Dispatch和Swift Concurrency不兼容.在编译期间,编译器只能尽最大努力找出当涉及Dispatch时代码是否在主线程上执行.

无论如何,我建议你重构你的类ProfileViewModel,使类作为一个整体成为独立的MainActor.Optionally,您可以判断是否在MainActor上执行:


@MainActor
final class ProfileViewModel: ObservableObject {
    let authService: AuthService
    
    @Published var userHandle: String = "Unknown"
    
    var cancellable: AnyCancellable!
  
    init(authService: AuthService, appState: AppState) {
        self.authService = authService
        cancellable = appState.$userData
            .receive(on: DispatchQueue.main)
            .map { $0 }
            .sink(receiveValue: { string in
                MainActor.assumeIsolated {   // <== Optional runtime check
                    self.userHandle = string
                }
        })
  }
}

注意,当您将队列更改为其他非主队列时,编译器不会发出任何错误或警告!

因此,您可能需要在运行时添加此判断:

MainActor.assumeIsolated { ... }

当你的代码在一个调度块中执行时.

Swift相关问答推荐

如何在HStack中均匀分布视图?

按下一步后无法让文本字段切换焦点

是否可以循环遍历SWIFT子类并访问它们的静态变量S?

在不传递参数的情况下使用Init方法?

与视图交互使工具栏&;标题消失

try 从闭包访问返回的字符串时出现无法将类型 '()' 的返回表达式转换为返回类型 'String'编译器错误

使用UICollectionViewFlowLayout创建水平的UICollectionView,并同时将单元格对齐到左边和右边的能力

为什么这段Swift读写锁代码会导致死锁?

如何判断一个值是否为 Int 类型

swift 是否遇到 Java 的 2gb 最大序列化大小问题?

SwiftUI 动画的范围

您是否必须手动指定您的 DispatchQueue 是串行的?

在 Vapor 4 中使用协议的通用流利查询

RxSwift 提高性能

使用协议捕获 SwiftUI 视图

将数据附加到 Firebase 实时数据库后,NavigationLink 将我重定向到上一个视图

Swift:连接两个数组而不重复共享后缀/前缀

Xcode 7.3 Swift 的语法高亮和代码完成问题

在 Swift 中以编程方式更新约束的常量属性?

swift中静态函数和单例类的区别