从Realm加载TableView数据后删除条目时出现‘Object has been deleted or invalidated’错误
解决Realm+UITableView删除时的「Object has been deleted or invalidated」错误
这个问题我之前在做Realm相关的TableView功能时也踩过坑,核心原因是Realm对象的绑定特性和你当前用的普通数组数据源不兼容。你把Realm的Results转成了普通Array,但Realm对象本质是和Realm实例绑定的懒加载对象,一旦被删除,数组里对应的对象就会变成无效状态,TableView再去访问它就会抛出这个错误。下面给你两种最常用的解决思路:
方案一:直接用Realm的Results作为TableView数据源(推荐)
Realm的Results是自动更新的集合,它会实时反映Realm里的数据变化,而且不会持有无效对象。步骤如下:
- 修改数据源类型:
把原来的普通数组数据源改成Results类型,并添加监听令牌:
var datasource: Results<ModelTask>? var notificationToken: NotificationToken? // 用来监听数据变化的令牌
- 加载数据并监听变化:
替换原来的加载逻辑,直接使用Results并添加监听:
// 直接赋值Results,不要转成Array datasource = realmManager.objects(ModelTask.self).filter("planId == %@", currentPlan.id) // 添加监听,数据变化时自动刷新TableView notificationToken = datasource?.observe { [weak self] change in guard let self = self else { return } switch change { case .initial, .update: self.tableViewMain.reloadData() case .error(let error): print("Realm监听错误:\(error)") } }
- 优化删除逻辑:
删除时只需要在Realm事务里执行删除,Results会自动更新,TableView也会通过监听自动刷新:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .destructive, title: "删除".localized) { [weak self] action, indexPath in guard let self = self, let task = self.datasource?[indexPath.row] else { return } // 必须在Realm事务里执行删除操作 do { try self.realmManager.realm.write { self.realmManager.realm.delete(task) } // 这里不需要手动刷新TableView,监听会自动处理 } catch { print("删除失败:\(error)") } } return [delete] }
- 清理监听避免内存泄漏:
在ViewController的销毁方法里移除监听令牌:
deinit { notificationToken?.invalidate() }
方案二:坚持用普通数组作为数据源(适合特殊场景)
如果因为业务原因必须用普通数组,那要确保删除时同步更新数组和Realm,并且不要访问已删除的对象:
- 修正删除逻辑:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .destructive, title: "删除".localized) { [weak self] action, indexPath in guard let self = self else { return } // 1. 先从数组里取出有效对象(此时还未删除) let task = self.datasource[indexPath.row] // 2. 在Realm事务里执行删除 do { try self.realmManager.realm.write { self.realmManager.realm.delete(task) } // 3. 同步从数据源数组移除对应元素 self.datasource.remove(at: indexPath.row) // 4. 刷新TableView对应行(比全局reload更高效) self.tableViewMain.deleteRows(at: [indexPath], with: .automatic) } catch { print("删除失败:\(error)") } } return [delete] }
- 关键注意事项:
- 绝对不要在删除Realm对象后,再去访问数组里的旧对象(比如获取属性或做判断)
- 每次重新加载数据时,都要从Realm重新查询并转成数组,不要复用旧数组
额外排查点
- 确保你的
realmManager持有的Realm实例和删除、查询用的是同一个 - Realm对象不是线程安全的,要确保只在创建它的线程里访问和操作
内容的提问来源于stack exchange,提问作者ice.hu
相关产品推荐
相关产品推荐

