如何让Realm对象遵循Sendable协议?解决Actor隔离数据传递问题
问题背景
Realm推出了actor-isolated realms特性,我们用@globalActor管理数据持久化(多场景需触发读写),但传递Realm对象时出现警告:
"Passing argument of non-sendable type 'any Message' into global actor 'RealmBackgroundActor'-isolated context may introduce data races"
排查后发现是自定义Message类未遵循Sendable协议,但尝试让它遵循时,编译器报错:
"'Sendable' class 'Message' cannot inherit from another class other than 'NSObject'"
当前需要解决的问题:
- 实现Actor与外部的数据传递(比如Actor内读取的Realm数据更新UI,或外部向Actor传对象执行写入)
- 确认
@globalActor的使用是否合理 - 不想采用“为每个Realm对象创建Sendable Swift对象,在Actor内转换”这种不够优雅的方案
相关代码示例
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 } }
Global Actor与持久化服务实现
@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) } } }
外部调用代码
await PersistanceService.shared.write(object: object)
解决方案
1. 遵循Realm设计意图:用值类型跨Actor传递数据
Realm的actor-isolated realms本身要求Realm实例和它管理的对象只能在绑定的Actor内部使用,不能直接跨Actor传递。最安全的做法是用Sendable的结构体作为数据载体:
首先定义Sendable的数据模型:
struct MessageModel: Sendable, Codable { let _id: String var senderUserName: String? var text: String }
修改持久化服务的方法,在Actor内部完成Realm对象和值类型的转换:
@RealmBackgroundActor class PersistanceService: NSObject { // ... 原有代码不变 func write(messageModel: MessageModel) { guard let realm = realm else { return } do { let message = Message() message._id = messageModel._id message.senderUserName = messageModel.senderUserName message.text = messageModel.text try realm.write { realm.add(message, update: .modified) } } catch { fatalError(error.localizedDescription) } } // 读取示例:返回Sendable的模型数组供UI使用 func fetchAllMessages() async -> [MessageModel] { guard let realm = realm else { return [] } let realmMessages = realm.objects(Message.self).map { MessageModel(_id: $0._id, senderUserName: $0.senderUserName, text: $0.text) } return Array(realmMessages) } }
外部调用时传递结构体即可:
let model = MessageModel(_id: UUID().uuidString, senderUserName: "Alice", text: "Hello") await PersistanceService.shared.write(messageModel: model)
2. 临时方案:用@unchecked Sendable绕过编译警告(谨慎使用)
如果不想额外定义结构体,可以通过条件扩展让Realm对象标记为Sendable,但需要严格保证这些对象不会跨Actor传递,仅在绑定的Actor内部使用:
extension Message: @unchecked Sendable {}
注意:
@unchecked Sendable是告诉编译器“我自行保证类型的线程安全性”,如果跨Actor传递Realm对象,依然会有数据竞争风险,仅建议在完全受控的场景下使用。
3. 优化Global Actor的使用
当前的RealmBackgroundActor作为global actor的用法是合理的,但需要注意:
setupRealm方法中try? await Realm(actor: ...)已经将Realm实例绑定到RealmBackgroundActor,@RealmBackgroundActor属性保证了所有Realm操作都在该Actor上下文中执行- 不要在Actor外部持有Realm实例或它的对象,所有操作都通过
PersistanceService的方法封装
总结
最安全且符合Realm设计意图的方案是使用值类型作为跨Actor的数据载体,虽然需要额外定义模型,但这是保证线程安全、符合Swift并发模型的标准做法。@unchecked Sendable可作为临时方案,但存在风险,不推荐在跨Actor场景下使用。
内容的提问来源于stack exchange,提问作者ambassador

