如何使领域对象可发送?

How to make realm objects Sendable?

提问人:ambassador 提问时间:10/9/2023 更新时间:10/9/2023 访问量:109

问:

TLDR:Realm 引入了 actor 隔离的领域,但领域对象不是 .那么,应该如何使用这些参与者孤立的领域呢?或者如何使我的对象可发送?Sendable


Realm 最近引入了 actor 隔离领域,我们正在将它与管理应用程序中的数据持久性一起使用,因为许多不同的地方需要触发写入/读取数据。@globalActor

当将此 actor 与任何正在传递的领域对象一起使用时,会弹出警告:

将不可发送类型“any Message”的参数传递到全局参与者“RealmBackgroundActor”隔离的上下文中可能会引入数据争用

我想这是由于我们的领域对象的实现不可发送:Message

class Message: Object, ObjectKeyIdentifiable, Codable {
    @Persisted(primaryKey: true) var _id: String
    @Persisted var senderUserName: String?
    @Persisted var text: String
    
    convenience init(_id: String = UUID().uuidString, senderUserName: String, text: String) {
           self.init()
            self._id = _id
           self.senderUserName = senderUserName
           self.text = text
       }
}

但我做不到,因为:Sendable

“Sendable”类“Message”不能从“NSObject”以外的其他类继承

如何向该参与者传递数据或从该参与者传递数据?我想实际使用在Actor内部和Actor外部读取的领域数据是很常见的,例如用于更新UI。或者将外部对象传递给执行者以执行写入。

旁注:我非常不确定我是否使用了预期的方式,但我不知道还有什么办法可以确保没有与领域数据进行数据竞争,而不必在任何地方手动注入/管理一些领域参与者。@globalActor

我的想法是为每个领域对象创建一个可发送的 swift 对象,仅在 actor 内部与领域对象之间转换,但这似乎不是很干净。


我们实施的:@globalActor

@globalActor
actor RealmBackgroundActor {
    static var shared = RealmBackgroundActor()
}

@RealmBackgroundActor
class PersistanceService: NSObject {
    
    static let shared: PersistanceService = PersistanceService()
    var realm: Realm?

    func setupRealm() async {
        realm = try? await Realm(actor: RealmBackgroundActor.shared)
    }

    func write(object: Object) {
        do {
            try realm.write {
                realm.add(object, update: .modified)
            }
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}

并像这样调用这个 actor 之外的 write 函数:

await PersistanceService.shared.write(object: object)
iOS Swift 领域

评论

0赞 Jay 10/10/2023
我不清楚这个问题;使用 asyncWrite 可以对 Realm 写入进行线程安全 - 它以线程安全的方式管理 Realm 访问,而无需您编写专门的代码来自己完成。此外,使用 (ThreadSafe)[mongodb.com/docs/realm/sdk/swift/crud/threading/...] 符合 Sendable,因此实现它将使对象能够在线程之间传递。也可以冻结对象以使用只读版本。但是用例是什么?听起来你想以线程安全的方式写入数据 - asyncWrite不是这样做的吗?
0赞 ambassador 10/10/2023
感谢您@Jay回复。试图坚持新的“参与者隔离领域”的一个原因是,它似乎更符合新的 Swift 并发语言,正如领域所说的那样“......如果您的应用程序使用 Swift 并发语言,则可能需要使用 actor 隔离领域...”例如,我只需要在专用 actor 上打开一次领域,并且根本不用担心异步调用,只要我留在这个 actor 中。Swift 管理线程。话虽如此,我也不确定,只是试图将所有与领域相关的事物隔离到一个地方,一个演员似乎很方便。
0赞 ambassador 10/10/2023
我经常在线程和处理领域/对象的奇怪地方遇到错误/崩溃,正如 realm 所写的那样:“如果您的应用程序在异步/等待上下文中访问 Realm,请用 @MainActor 标记代码以避免与线程相关的崩溃。 - 现在终于有了 10.39.0 版本,我不必总是使用 MainActor / Main 线程, 但可以有一个领域参与者来消除所有这些崩溃和问题。要是我能弄清楚 realm 打算如何充分利用这些演员就好了......
0赞 Jay 10/10/2023
好信息!我同意在这一点上用法有点模糊。我认为信息是,如果您使用的是 await/async,那么最好的选择是利用参与者。但是根据您上面的代码和注释,听起来不像您使用 await/async 调用,那么演员需要什么?另外,请注意 - Realm 没有提供 Swift async/await 对读取或写入对象的支持 - 但它们可以使用后台写入安全地执行。那么,为什么要强行采用这种模式呢?线程问题通常只是一个逻辑问题。所以回到问题;Sendable 需要什么?

答: 暂无答案