ios - 通过不同的视图控制器使用 Realm 时出现 RLMException
问题描述
我正在使用 Realm 快速创建要保存在用户设备上的收藏产品列表。
简而言之,2 个视图控制器(A 和 B)嵌入在一个标签栏控制器中。
- 视图控制器 A 显示从 Firebase 数据库获取的一些产品。
视图控制器 A 还有一个“添加到收藏夹”按钮,用于将产品添加到 Realm 实例。
视图控制器 B 嵌入了一个表视图来显示来自这个 Realm 实例的结果。
问题是当我从表视图中删除一些产品时,我一回到视图控制器 A 就会收到以下错误。
Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated
我已经读到,当尝试从 Realm 访问已删除的对象或尝试重新添加以前删除的对象时会发生此错误。这似乎不是这里的情况。
谢谢你的任何建议。
这是我的视图控制器 A 中的“添加到收藏夹”按钮:
@IBAction func addToFavorites(_ sender: Any) {
let realm = try! Realm()
try! realm.write {
realm.add(currentProduct)
}
}
这是我的视图控制器 B 中的表格视图代码:
class ViewControllerB: UIViewController, UITableViewDataSource, UITableViewDelegate {
let realm = try! Realm()
var favorites: Results<Product>!
@IBOutlet weak var favoritesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
favoritesTableView.delegate = self
favoritesTableView.dataSource = self
favoritesTableView.rowHeight = 70
}
override func viewWillAppear(_ animated: Bool) {
favorites = realm.objects(Product.self)
favoritesTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! favoriteTableViewCell
let product = favorites[indexPath.row]
cell.productName.text = product.name
cell.productCategory.text = product.category
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let product = favorites[indexPath.row]
try! realm.write {
realm.delete(product)
}
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)
}
}
编辑: 这是完整的视图控制器 A
import UIKit
import RealmSwift
class ViewControllerA: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var products = [Product]()
var scannedProduct = Product()
var currentProduct = Product()
let realm = try! Realm()
@IBOutlet weak var myCollectionView: UICollectionView!
@IBAction func addToFavorites(_ sender: Any) {
try! realm.write {
realm.add(currentProduct)
}
}
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.dataSource = self
myCollectionView.isPagingEnabled = true
myCollectionView.backgroundColor = UIColor.blue
layout()
}
override func viewDidAppear(_ animated: Bool) {
currentProduct = scannedProduct
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return products.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! collectionCell
cell.productName.text = products[indexPath.item].name
cell.codeLabel.text = products[indexPath.item].code
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: myCollectionView.bounds.width, height: myCollectionView.bounds.height)
}
func layout() {
if let flowLayout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let currentVisibleCell = myCollectionView.visibleCells.first
let currentIndex = myCollectionView.indexPath(for: currentVisibleCell!)?.item
currentProduct = products[currentIndex!]
}
}
解决方案
当尝试从 Realm 访问已删除的对象时,这里似乎并非如此。
实际上...
try! realm.write { realm.delete(product) } tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)
这两行不应该是这样的。你看,Realm 有一个叫做 a 的东西NotificationToken
,并且能够得到observe
a 结果。根据示例,它应该如下所示:
var notificationToken: NotificationToken?
override func viewDidLoad() {
...
// Set results notification block
self.notificationToken = results.observe { (changes: RealmCollectionChange) in
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
self.tableView.reloadData()
break
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the TableView
self.tableView.beginUpdates()
self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.endUpdates()
break
case .error(let err): // this is probably not relevant in a non-sync scenario
break
}
}
如果你有这个,那么理论上你可以删除你自己的tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)
电话。每当发生更改(写入发生在 Realm 上)时,此观察者都会使 TableView 保持最新。
推荐阅读
- python - 如何调用通过 tkinter filedialog 导入的文件?
- reactjs - 使用相同的组件多次使用相同的 reducer 和每个被调用组件的状态变量实例
- microsoft-graph-api - ADB2C Graph API:登录历史仅回溯 7 天
- android - 从字符串加载html时TWebBrowser抛出线程异常
- powershell - 在 Exchange Powershell 中设置邮件转发
- cassandra - 将带有数据的 Cassandra 密钥空间导出到文件
- java - 从命令行运行 skdmanager 会产生 java 异常
- java - 如何从 RequestBody 在自定义注解中注入数据
- c++ - 构造函数初始值设定项列表中长度未知的数组
- python - 熊猫删除括号之间的字符