我看到的崩溃要么不应该发生,要么很有可能发生,而且文档还不够清楚地说明原因。
尽管我不同意下面要求我将其分成多个 SO 问题的评论,但如果有人可以专注于这个问题,我认为这将有很大帮助:
通知何时发送到主线程?主线程上的结果是否可能与之前的 runloop 中的结果不同,而还没有得到通知?
如果这个问题的答案是肯定的,结果可能与之前的 runloop 不同而没有通知,那么我认为将其放入文档中的某个地方是至关重要的。
首先,我认为回顾一下我已经在为写作所做的事情很重要。我所有的写入都是通过一种基本上如下所示的方法执行的(除了错误处理):
func write(block: @escaping (Realm) -> ()) {
somePrivateBackgroundSerialQueue.async {
autoreleasepool {
let realm = try! Realm()
realm.refresh()
try? realm.write { block(realm) }
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里没什么疯狂的,在你的最后有很好的记录。
我在这里的主要问题是从后台线程写入通知后,何时将通知传送到主线程?我有一个由领域结果支持的复杂表格视图(多个部分,每 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) ...
Run Code Online (Sandbox Code Playgroud)
在构建我的部分数组之前,我获取领域结果并根据特定屏幕的故事类型对其进行过滤,对它们进行排序,以便它们的部分顺序正确,然后我通过传递results.filter(...date query...)给部分构造函数来构建部分. 最后,我results.observe(...)是主要结果对象(不是任何传递到该部分的结果)并在调用通知处理程序时重新加载表视图。我不会费心观察各部分中的结果,因为如果这些结果中的任何一个发生变化,那么父级也必须更改,并且应该触发更改通知。
当广告被填满或未填满时,广告位有回调,当发生这种情况而不是调用时,tableView.reloadData()我正在做类似的事情:
guard tableView.indexPathsForVisibleRows?.contains(indexPath) == true else { return }
tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
Run Code Online (Sandbox Code Playgroud)
问题是,在访问领域结果或无效的表视图更新时,我很少看到因索引越界而导致的崩溃。
问题
reloadData()不能在领域通知块之外的任何地方使用表视图更新之外,还应该使用表视图更新吗?你的代码片段或你正在做的事情的描述中没有任何东西让我觉得明显是错误的。拥有一个独立于领域更改通知更新特定插槽的单独回调机制,很可能会出现与计时相关的错误,但由于您在重新加载行之前显式检查 indexPath 是否可见,因此我预计最坏的情况会表现为重新加载错误的行而不是崩溃。
预期的行为是刷新领域和传递通知是一个原子操作:任何导致读取版本前进的操作都将在返回之前传递所有通知。在简单的情况下,这意味着如果没有首先触发关联的通知,您将永远不会看到新数据。但是,有一些注意事项:
嵌套通知传递无法正常工作,因此从通知块内开始写入事务可能会导致通知被跳过(仅调用refresh()不会导致这种情况,因为它只是通知中的无操作)。如果您在后台线程上执行所有写入操作,则不应遇到此问题。
如果您有多个通知块,那么显然从第一个通知块调用的任何内容都会在第二个通知块有机会执行操作之前运行,并且调用tableView.reloadData()可能会导致通知块内发生很多事情。如果这是问题的根源,您可能会看到通知块内抛出的堆栈跟踪异常。
| 归档时间: |
|
| 查看次数: |
517 次 |
| 最近记录: |