【问题标题】:Realm Swift: Object has been deleted or invalidatedRealm Swift:对象已被删除或失效
【发布时间】:2020-05-29 14:10:19
【问题描述】:

我有一个 tableViewController (ArticlesVC),它显示从 API 获取的文章数组。这些 tableViewCell 中的每一个都有一个“更多操作”按钮,允许用户保存文章以供以后在 SavedVC 中阅读。请注意,只有保存的文章会写入 Realm DB,ArticlesVC 中显示的文章不会写入 Realm DB。

如果用户保存了文章,我会显示“删除保存的文章”,否则,我会显示“稍后保存”。我查询 Realm 进行此检查。

但是,如果我从 SavedVC 的 Realm 中删除已保存的文章(或所有已保存的文章)并返回到 ArticlesVC 并开始滑动 tableView,它会因上述错误而崩溃。这种崩溃是间歇性发生的,因此很难确定。

代码

//At ArticlesVC, where articles are fetched via API
func getArticles() {
    AF.request(apiUrl).responseJSON { (response) in
        //Error checks and decoding ...

        self.articles = try decoder.decode(Article.self, from: data)
    }
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ArticleCell
    cell.article = articles[indexPath.row]
    cell.moreButton.tag = indexPath.row
    cell.moreButton.addTarget(self, action: #selector(moreButtonTapped(sender:)), for: .touchUpInside)
    return cell
}

@objc func moreButtonTapped(sender: UIButton) {
    let buttonTag = sender.tag
    let article = articles[buttonTag]

    let alert = UIAlertController(title: "More", message: nil, preferredStyle: .actionSheet)
    let action = getAction(article: article)
    alert.addActions([action, .cancelAction()])
    //present alert
}


func getAction(article: Article) -> UIAlertAction {

    do {
        let realm = try Realm()
        let articleInRealm = realm.objects(Article.self).filter("id == %@", article.id)

        if articleInRealm.isInvalidated {
            return UIAlertAction(title: "Save for later", style: .default, handler: {(_) in
                self.saveArticle(article: article)
            })
        } else {
            if articleInRealm.count == 0 {
                return UIAlertAction(title: "Save for later", style: .default, handler: {(_) in
                    self.saveArticle(article: article)
                })
            } else {
                return UIAlertAction(title: "Remove saved article", style: .destructive, handler: {(_) in
                    self.deleteArticle(article: articleInRealm)
                })
            }
        }


    } catch {
        Log("Err getting article: \(error.localizedDescription)")
        return UIAlertAction(title: "Save for later", style: .default, handler: {(_) in
            self.saveArticle(article: article)
        })
    }
}

func saveArticle(article: Article) {
    do {
        let realm = try Realm()

        try realm.write {
            realm.add(article, update: .modified)
        }

    } catch {
        Log("Err saving article: \(error.localizedDescription)")
    }
}

func deleteArticle(article: Results<Article>) {
    do {
        let realm = try Realm()

        try realm.write {
            realm.delete(article)
        }

    } catch {
        Log("Err saving article: \(error.localizedDescription)")
    }
}

//At SavedVC, which is another VC in the tabBarController. 
//ArticlesVC is in tab1 and SavedVC is in tab2
var notificationToken: NotificationToken? = nil
var articles: Results<Article>?

override func viewDidLoad() {
    super.viewDidLoad()

    subscribeToRealmNotifications()
}

fileprivate func subscribeToRealmNotifications() {
    let realm = try! Realm()
    let results = realm.objects(Article.self)

    notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in

        guard let tableView = self?.tableView else { return }

        switch changes {
            case .initial:

                self?.articles = results

                tableView.reloadData()

            case .update(_, _, _, _):
                tableView.reloadData()

            case .error(let error):
                fatalError("Realm notif: \(error.localizedDescription)")
        }
    }
}

我的猜测是,因为我已经删除了 SavedVC 中的文章,但 ArticlesVC 仍然指向那些不再存在并因此崩溃的 Realm 对象。

我已经阅读了多篇建议对obj.isInvalidated 进行检查的 SO 帖子,我已经这样做了,并且为此添加了一个条件。但这仍然崩溃。

其他尝试包括:

  • 使用realm.create() 代替realm.add()

【问题讨论】:

  • 几件事。领域结果对象是实时更新列表 - 如果从领域中删除一个对象,该结果列表也将被删除。这个函数定义有点麻烦func deleteArticle(article: Results&lt;Article&gt;) 好像你打算删除一篇文章,为什么要传入一个从技术上讲可能不止一个对象的结果对象?

标签: ios swift realm


【解决方案1】:

问题在于:您已将非托管对象(文章)保存到领域

func getAction(article: Article)
    ...
    self.saveArticle(article: article)

然后使它成为一个托管对象。

如果从 Realm 中删除,

realm.delete(article)

它也将从包含它的结果对象中删除

articles[indexPath.row]

这将使您的应用在滚动时崩溃,因为 dataSource 和 tableView 不同步。

我有一种感觉,您不希望它实际上从文章主列表中删除。

我的建议是保存任何您想稍后阅读的文章,因为它们在添加和从领域中删除时不会对文章的主列表产生影响。

换句话说,您的文章主列表保持不变,但在 Realm 中,您有一个 id 表,例如字符串。所以当你想跟踪他们想要阅读的内容时

self.saveArticleId(article: article.id)

然后是时候从他们保存的阅读列表中删除它

self.deleteArticleBy(articleId: artical.id)

【讨论】:

  • 感谢您的输入,但我想澄清一下,文章的主列表不是 Realm 对象,即主列表中的文章是 [Article]() 而不是 Results&lt;Article&gt;。它们只是从 API 返回并列出,而不是通过 Realm 管理。只有那些用户想要保存以供以后阅读的文章才通过 Realm 进行管理。这应该不会影响articles[indexPath.row] 吧?
  • 另外,通过articleId 保存到 Realm 意味着需要进行另一个 API 调用来获取文章数据,这不是我们想要的。我们希望使文章可以离线访问,从而使用 Realm 实现。
  • @Koh um...如果它不是领域对象,那么您如何将其保存到领域?如果您查看流程,代码会将非领域Article 对象传递给func getAction(article: Article)。然后将相同的非领域Article 对象传递给self.saveArticle(article: article),然后将其保存到领域realm.add(article, update: .modified)。所以...?
  • @Koh 并用问题回答您评论中的问题...保存的文章如何显示给用户,以便他们知道它们已保存?换句话说,当用户想要阅读已保存的文章时会发生什么顺序?假设他们有 100 个。您是在一英里长的列表中显示每篇文章的全部内容,还是只是第一句话的 sn-p 或文章标题?那么,当他们选择阅读哪篇文章时,接下来会发生什么?这有点过时,但答案将取决于该信息。让我们先解决最初的问题。
  • 感谢您的回复。是的,在realm.add(article, update: .modified) 之后,我们会显示一个成功保存的弹出窗口。保存的文章将显示在另一个 tableView (SavedVC) 中。我添加了代码来显示如何在 SavedVC 中从 Realm 读取已保存的文章。基本上我们会在 SavedVC 中的 tableView 中显示关键信息,即标题、发布日期、imageView 等,当用户点击文章时,它会推送到 detailVC 以显示完整文章的内容。我们设计者的意图是让 SavedVC 完全离线。
猜你喜欢
  • 2015-06-08
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多