在后台线程上写入后,Realm通知何时传递到主线程?

时间:2017-11-09 05:37:16

标签: swift realm

我发现崩溃不应该是可能的,或者是非常可能的,而且文档还不够清楚原因。

更新

虽然我不同意下面的评论,要求我将其分成多个SO问题,如果有人可以专注于这个问题,我认为这会有很大帮助:

通知何时传递到主线程?主线程上的结果是否可能与之前的runloop中的结果不同而未通知其差异?

如果这个问题的答案是是,那么结果可能与之前的runloop不同而没有通知那么我认为将其纳入文档的某些地方是不可能的。

后台写入

首先,我认为重新审视我已经为写作做的事情是非常重要的。我的所有写操作都是通过一个基本上看起来像这样的方法执行的(除了错误处理):

func write(block: @escaping (Realm) -> ()) {
    somePrivateBackgroundSerialQueue.async {
        autoreleasepool {
            let realm = try! Realm()
            realm.refresh()
            try? realm.write { block(realm) }
        }
    }
}

这里没有什么可以疯狂的,你的文章记录得很清楚。

通知和表格视图

我在这里遇到的主要问题是是从后台线程写入后传递给主线程的通知吗?我有一个复杂的表格视图(多个部分,每5行广告)由领域结果支持。我的主要数据源如下:

enum StoryRow {
    case story(Story) // Story is a RealmSwift.Object subclass
    case ad(Int)
}

class StorySection {
    let stories: Results<Story>

    var numberOfRows: Int {
        let count = stories.count
        return count + numberOfAds(before: count)
    }

    func row(at index: Int) -> StoryRow {
        if isAdRow(at: index) {
            return .ad(index)
        } else {
            let storyIndex = index - numberOfAds(before: index)
            return .story(stories[storyIndex])
        }
    }
}

var sections: [StorySection]

... sections[indexPath.section].row(at: indexPath.row) ...

在构建我的sections数组之前,我获取领域结果并根据特定屏幕的故事类型对它们进行过滤,对它们进行排序,使它们按照正确的顺序排列,然后我通过传入{来构建这些部分{1}}到部分构造函数。最后,我results.filter(...date query...)主要结果对象(不是传递给该部分的任何结果)并在调用通知处理程序时重新加载表视图。我不打算在各节中观察结果,因为如果这些结果中的任何一个发生了变化,那么父母也必须改变,触发更改通知。

当广告被填充或未填充时,广告位会有回调,当发生这种情况而不是调用results.observe(...)时,我会执行以下操作:

tableView.reloadData()

问题是,在访问领域结果或无效的表视图更新时,我很少看到索引出现问题。

问题

  1. 在发送任何通知之前,是否有可能在主线程上更改了域?
  2. 除了guard tableView.indexPathsForVisibleRows?.contains(indexPath) == true else { return } tableView.beginUpdates() tableView.reloadRows(at: [indexPath], with: .automatic) tableView.endUpdates() 以外的表视图更新是否应该只在领域通知块之外的任何地方使用?
  3. 我还缺少其他重要的东西吗?

1 个答案:

答案 0 :(得分:2)

你的代码片段中没有任何内容或者你所做的事情的描述显然是错误的。具有单独的回调机制来更新独立于Realm更改通知的特定插槽有很多可能与计时相关的错误,但由于您在重新加载行之前显式检查indexPath是否可见,我希望最坏表现为重新加载错误的行而不是崩溃。

预期的行为是刷新Realm并发送通知是一种原子操作:导致读取版本前进的任何内容都将在返回之前传递所有通知。在简单的情况下,这意味着如果没有先关联的通知,您将永远不会看到新数据。但是,有一些警告:

嵌套通知传递无法正常工作,因此从通知块中开始写入事务可能会导致跳过通知(仅调用refresh()会导致此错误,因为它&# 39;只是通知中的无操作)。如果你在背景线程上执行所有写操作,那么你就不应该这样做。

如果您有多个通知块,那么显然从第一个通知块中调用的任何内容都会在第二个通知块有机会执行之前运行,并调用tableView.reloadData()可能会导致通知块中发生很多事情。如果这是问题的根源,那么您可能希望看到来自通知块内的堆栈跟踪引发异常。