首页 > 解决方案 > 通过不同的视图控制器使用 Realm 时出现 RLMException

问题描述

我正在使用 Realm 快速创建要保存在用户设备上的收藏产品列表。

简而言之,2 个视图控制器(A 和 B)嵌入在一个标签栏控制器中。

问题是当我从表视图中删除一些产品时,我一回到视图控制器 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!]
}
}

标签: iosswiftrealm

解决方案


当尝试从 Realm 访问已删除的对象时,这里似乎并非如此。

实际上...

    try! realm.write {
        realm.delete(product)
    }
    tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)

这两行不应该是这样的。你看,Realm 有一个叫做 a 的东西NotificationToken,并且能够得到observea 结果。根据示例,它应该如下所示:

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 保持最新。


推荐阅读