Realm删除SwiftUI列表行报错:对象已删除或失效,如何修复?
解决Realm删除后SwiftUI List报错“Object has been deleted or invalidated”的问题
嘿,我之前也踩过这个Realm+SwiftUI的坑,咱们来聊聊问题根源和解决办法:
问题出在哪?
你现在用了@Published var aquaris: [Aquaris]这个普通数组来存储Realm对象,虽然设置了NotificationToken监听变更,但有两个关键问题:
- 当你删除Realm对象后,原来数组里的那个对象会变成无效状态(已经被Realm标记为删除),但SwiftUI的
ForEach还在尝试引用它(因为你用了id: \.self,而这个对象本身已经失效),所以触发了RLMException。 - 手动把Realm的
Results转成普通数组的操作,可能和Realm的自动更新机制不同步,导致视图刷新不及时,残留了无效对象的引用。
怎么解决?
核心思路是直接使用Realm的Results集合,而不是转成普通数组,因为Results是Realm提供的自动更新、懒加载的集合,能完美配合SwiftUI的响应式系统。下面是修改后的完整代码:
1. 修正DadesRealm类
把存储数据的类型改成Results<Aquaris>,并调整监听逻辑:
import SwiftUI import RealmSwift final class DadesRealm: ObservableObject { @Published var aquaris: Results<Aquaris> private var cargaToken: NotificationToken? init() { let realm = try! Realm() aquaris = realm.objects(Aquaris.self) recargarDatos() } private func recargarDatos() { // 监听Results的变更,一旦有变化就通知SwiftUI刷新视图 cargaToken = aquaris.observe { [weak self] _ in guard let self = self else { return } self.objectWillChange.send() } } deinit { cargaToken?.invalidate() } }
2. 修正List和删除逻辑
把ForEach的id改成Realm对象的主键(你已经设置了id作为主键),同时优化删除的错误处理:
List{ ForEach(datosRealm.aquaris, id: \.id) { item in Text(item.nombre) }.onDelete { (indexSet) in guard let index = indexSet.first else { return } let aqua = datosRealm.aquaris[index] let realm = try! Realm() do { try realm.write{ realm.delete(aqua) } } catch { print("删除操作失败:\(error.localizedDescription)") } } }
为什么这样改能解决问题?
Results自动更新:Realm的Results会在数据发生变更(比如删除、新增、修改)时自动同步,不需要手动转数组,从根源避免了无效对象留在数组里的问题。- 用主键作为id:
id: \.id使用了对象的唯一主键,即使对象被删除,主键值依然有效,SwiftUI能正确识别列表项的增删变化,不会尝试访问已失效的对象实例。 - 可靠的视图刷新:通过
objectWillChange.send()主动通知SwiftUI视图刷新,确保视图和Realm数据状态完全同步。
这样修改后,你删除列表项时,Realm会立即移除对象,Results自动更新,SwiftUI也会同步刷新列表,不会再出现那个讨厌的RLMException啦!
内容的提问来源于stack exchange,提问作者Miquel Molina
相关产品推荐
相关产品推荐

