You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从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里的数据变化,而且不会持有无效对象。步骤如下:

  1. 修改数据源类型:
    把原来的普通数组数据源改成Results类型,并添加监听令牌:
var datasource: Results<ModelTask>?
var notificationToken: NotificationToken? // 用来监听数据变化的令牌
  1. 加载数据并监听变化:
    替换原来的加载逻辑,直接使用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)")
    }
}
  1. 优化删除逻辑:
    删除时只需要在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]
}
  1. 清理监听避免内存泄漏:
    在ViewController的销毁方法里移除监听令牌:
deinit {
    notificationToken?.invalidate()
}

方案二:坚持用普通数组作为数据源(适合特殊场景)

如果因为业务原因必须用普通数组,那要确保删除时同步更新数组和Realm,并且不要访问已删除的对象:

  1. 修正删除逻辑:
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]
}
  1. 关键注意事项:
  • 绝对不要在删除Realm对象后,再去访问数组里的旧对象(比如获取属性或做判断)
  • 每次重新加载数据时,都要从Realm重新查询并转成数组,不要复用旧数组

额外排查点

  • 确保你的realmManager持有的Realm实例和删除、查询用的是同一个
  • Realm对象不是线程安全的,要确保只在创建它的线程里访问和操作

内容的提问来源于stack exchange,提问作者ice.hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:20:48