Swift中恢复已删除Realm对象失败求助
问题描述
我曾将对象添加至Realm数据库且成功删除,但重新添加时触发报错:
Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'PropertyImage' with an existing primary key value '3'。'
主键为3的对象已删除,且查看RealmDB确认其不存在,请问该如何解决?以下是我的数据库增删代码:
func delete<T: Object>(_ object: T, title: String) where T: Codable { do { let predicate = NSPredicate(format: "title=%@", title) guard let toRemove = realm?.objects(T.self).filter(predicate) else { return} try realm?.write { realm?.delete(toRemove) } } catch { } } func create<T: Object>(_ objects: [T]) where T: Codable { do { try realm?.write { realm?.add(objects) } } catch { print(error.localizedDescription) } }
添加调用:var property: FeaturedProperties? RealmManager.instance.create(property)
删除调用:RealmManager.instance.delete(property, title: property.title!)
问题分析
从你的报错和代码来看,核心矛盾是你认为主键3的对象已删除,但Realm判定该主键仍存在,可能的原因有这些:
- 删除操作未真正执行成功:你的delete函数吞掉了所有异常,可能删除时已经抛出错误但你没察觉(比如Realm实例为nil、权限问题、事务提交失败)。
- Predicate匹配错误:用
title=%@作为删除条件,可能存在title大小写、空格或者编码差异,导致实际没匹配到要删除的对象,主键3的对象其实还在数据库里。 - Realm实例不一致:如果你的
realm?是多个不同实例,删除操作在一个实例执行,但添加时用了另一个未同步的实例(Realm默认实时同步,这种概率较低,但仍需排查)。 - 内存中旧对象未释放:虽然数据库里的对象被删除,但你内存中持有的
property还是旧实例,重新添加时其主键仍是3,会触发Realm的唯一性校验。
解决方案
针对上述可能的问题,逐一排查修复:
1. 先确认删除操作是否真的成功
修改delete函数,不再吞掉异常,打印关键日志确认执行情况:
func delete<T: Object>(_ object: T, title: String) where T: Codable { do { guard let realm = realm else { print("Realm实例为空,无法执行删除操作") return } let predicate = NSPredicate(format: "title=%@", title) let toRemove = realm.objects(T.self).filter(predicate) print("匹配到\(toRemove.count)个待删除对象,标题:\(title)") // 确认是否找到目标对象 try realm.write { realm.delete(toRemove) print("删除操作执行成功") } } catch { print("删除失败,错误信息:\(error.localizedDescription)") // 打印错误 } }
执行删除后,查看日志:如果匹配数量为0,说明你的title条件和数据库中对象的title不完全一致;如果有错误信息,根据提示修复问题。
2. 修改create函数,允许主键更新(按需使用)
即使确认对象已删除,偶尔可能因为Realm缓存或同步延迟触发报错,你可以在添加时加上update: .modified参数,这样如果主键存在就更新,不存在就创建:
func create<T: Object>(_ objects: [T]) where T: Codable { do { guard let realm = realm else { print("Realm实例为空,无法执行添加操作") return } try realm.write { realm.add(objects, update: .modified) // 添加update参数 } } catch { print("添加失败,错误信息:\(error.localizedDescription)") } }
注意:这个方案适合需要复用主键的场景,如果业务逻辑要求主键唯一且不可复用,还是要先确保删除操作真正执行。
3. 确保Realm实例全局唯一
检查你的RealmManager中realm属性的初始化方式,确保是全局单例实例:
class RealmManager { static let instance = RealmManager() private(set) var realm: Realm? private init() { do { realm = try Realm() } catch { print("Realm初始化失败:\(error)") } } }
避免在多个地方初始化不同的Realm实例,导致操作不同步。
4. 重新创建对象实例,不要复用内存中的旧对象
如果你的property是之前删除的旧实例,即使数据库里已删除,内存中的对象主键仍是3,重新添加会触发校验。建议重新创建新对象:
// 不要复用旧的property对象,重新创建新实例 let newProperty = FeaturedProperties() newProperty.id = 3 // 假设主键字段是id newProperty.title = "你的标题内容" // 其他属性赋值 RealmManager.instance.create([newProperty])
额外验证步骤
如果以上方法都没解决,直接通过主键查询数据库,确认对象是否真的被删除:
// 在删除后、添加前执行这段代码 if let realm = RealmManager.instance.realm { let existingObject = realm.object(ofType: PropertyImage.self, forPrimaryKey: 3) if existingObject != nil { print("糟了!主键为3的对象还在Realm里!") } else { print("主键为3的对象确实已被删除") } }
这段代码比用title查询更准确,能帮你确认数据库中的真实状态。
内容的提问来源于stack exchange,提问作者King

