RLMException при использовании Realm через разные контроллеры представления - PullRequest
0 голосов
/ 05 июля 2018

Я использую Realm в swift, чтобы создать список избранных продуктов для сохранения на устройстве пользователя.

Короче говоря, 2 контроллера вида (A и B) встроены в контроллер панели вкладок.

  • View controller A отображает некоторые продукты, которые получены из базы данных Firebase.
  • Просмотр контроллера A также имеет кнопку «Добавить в избранное», которая добавляет продукт в экземпляр Realm.

  • Контроллер представления B встраивает представление таблицы для отображения результатов этого экземпляра Realm.

Проблема в том, что когда я удаляю некоторые продукты из табличного представления, я получаю следующая ошибка, как только я вернусь к просмотру контроллера А.

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

Я читал, что эта ошибка возникает при попытке получить доступ к удаленным объектам из области или при попытке повторно добавить ранее удаленный объект. Это не похоже на случай здесь.

Спасибо за любой совет.

Вот кнопка «Добавить в избранное» в моем 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)
    }
}

EDIT: Вот полный просмотр контроллера 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 голосов
/ 05 июля 2018

при попытке доступа к удаленным объектам из области, похоже, что это не так.

На самом деле ...

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

Эти две строки не должны выглядеть так. Видите ли, у Царства есть эта вещь, называемая 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) звонки. Этот наблюдатель будет поддерживать TableView в актуальном состоянии всякий раз, когда происходит изменение (запись происходит в Realm).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...