通过不同的视图控制器使用Realm时出现RLMException

时间:2018-07-04 21:02:55

标签: ios swift realm

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

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

  • 视图控制器A显示一些从Firebase数据库获取的产品。
  • 视图控制器A还具有一个“添加到收藏夹”按钮,用于将产品添加到Realm实例。

  • 视图控制器B嵌入表视图以显示该Realm实例的结果。

问题是,当我从表格视图中删除某些产品时, 我回到查看控制器A时会遇到以下错误。

Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated

我已经阅读到,当尝试从Realm访问已删除的对象时,或者尝试重新添加以前删除的对象时,会发生此错误。这里似乎并非如此。

谢谢您的建议。

这是我的View Controller 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!]
}
}

1 个答案:

答案 0 :(得分:0)

  

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

实际上...

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

这两行看起来不应该是这样。您会看到,Realm拥有NotificationToken这个东西,并且有observe个结果的能力。根据示例,它应如下所示:

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