gpt4 book ai didi

ios - UITableView reloadData 在 iOS 11 中重新出现时崩溃

转载 作者:技术小花猫 更新时间:2023-10-29 11:04:35 24 4
gpt4 key购买 nike

更新 :在我看来,这个问题仍然是相关的,所以我标记了我在代码中存在的潜在设计缺陷。我在 viewWillAppear: 中调用了异步数据填充方法。 VC1 是 从不 除非一切都在主线程中序列化,否则填充数据和重新加载 TableView 的好地方。当您必须重新加载 TableView 和 viewWillAppear 时,代码中总会有潜在的执行点。是 不是 其中之一。我总是在 VC1 viewWillAppear 中重新加载 TableView 数据源从 VC2 返回时。但理想的设计可以使用 VC2 的展开转场,并仅在实际需要时才从 VC2 准备数据源 (prepareForSegue) 重新填充数据源。不幸的是,到目前为止似乎没有人提到它:(

我想以前也有人问过类似的问题。不幸的是,他们都没有从本质上解决我面临的问题。

我的问题结构很简单。我有两个 View Controller ,比如 VC1 和 VC2。在 VC1 中,我在 UITableView 中显示从数据库加载的一些项目的列表,在 VC2 中,我显示所选项目的详细信息,并让它被编辑和保存。当用户从 VC2 返回到 VC1 时,我必须重新填充数据源并重新加载表。 VC1 和 VC2 都嵌入在 UINavigationController 中。

听起来很微不足道,确实如此,直到我在 UI 线程中完成所有操作。问题是在 VC1 中加载列表有点耗时。因此,我必须将繁重的数据加载任务委托(delegate)给某个后台工作线程,并仅在数据加载完成时才在主线程上重新加载表,以提供流畅的 UI 体验。所以我最初的构造类似于以下内容:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
dispatch_async(self.application.commonWorkerQueue, ^{
[self populateData]; //populate datasource
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData]; //reload table view
});
});
}

UITableView 开始,这在 iOS10 之前非常有用。通过 reloadData 停止立即渲染并开始治疗 reloadData就像重新加载 UITableView 的注册请求一样在运行循环的一些后续迭代中。所以我发现如果 [self.tableView reloadData] 我的应用程序开始偶尔崩溃在后续调用 [self populateData] 之前尚未完成从 [self populateData] 开始,这一点非常明显不再是线程安全的,并且如果数据源在 reloadData 完成之前发生更改应用程序很可能崩溃。所以我尝试添加一个信号量来制作 [self populateData]线程安全,我发现它工作得很好。我随后的构造类似于以下内容:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
dispatch_async(self.application.commonWorkerQueue, ^{
[self populateData]; //populate datasource
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData]; //reload table view
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(self.datasourceSyncSemaphore); //let the app know that it is free to repopulate datasource again
});
});
dispatch_semaphore_wait(self.datasourceSyncSemaphore, DISPATCH_TIME_FOREVER); //wait on a semaphore so that datasource repopulation is blocked until tableView reloading completes
});
}

不幸的是,这个结构在 iOS11 之后也被破坏了。当我向下滚动 UITableView 在 VC1 中,选择一个显示 VC2 的项目,然后返回 VC1。它再次调用 viewWillAppear: VC1 依次尝试通过 [self populateData] 重新填充数据源.但是崩溃的堆栈跟踪表明 UITableView 已经开始从头开始重新创建其单元格并调用 tableView:cellForRowAtIndexPath:出于某种原因的方法,甚至在 viewWillAppear: 之前,我的数据源正在后台重新填充,并且处于某种不一致的状态。最终应用程序崩溃。 最令人惊讶的是,只有当我选择了一个不在屏幕上的底行时才会发生这种情况,最初是 .以下是崩溃期间的堆栈跟踪:

crashed stack trace

我知道如果我从主线程调用这两种方法,一切都会运行良好,如下所示:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self populateData]; //populate datasource
[self.tableView reloadData]; //reload table view
}

但这并不是良好的用户体验所期望的。
我觉得这个问题发生在 UITableView向下滚动时,试图在重新出现时获取屏幕外的顶行。但不幸的是,在了解了这么多该死的事情之后,我几乎无法理清。

我真的很希望这个网站的专家能帮助我摆脱这种情况或向我展示一些方法。提前感谢您!

PS: self.application.commonWorkerQueue是在此上下文中在后台运行的串行调度队列。

最佳答案

您应该拆分您的 populateData功能。比如说 fetchDatabaseRowspopulateDataWithRows . fetchDatabaseRows应该在自己的线程和新的数据结构中将行检索到内存中。当 IO 部分完成后,您应该调用 populateDataWithRows (然后是 reloadData )在 UI 线程中。 populateDataWithRows应该修改 TableView 使用的集合。

关于ios - UITableView reloadData 在 iOS 11 中重新出现时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46742039/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com