tableView(_:cellForRowAt:) 与 com.apple.main-thread EXC_BREAKPOINT 崩溃
tableView(_:cellForRowAt:) crashes with com.apple.main-thread EXC_BREAKPOINT
Firebase Crashlytics 日志中有一个经常发生的奇怪崩溃,它说:
Crashed: com.apple.main-thread
0 AppName 0xd454 SomeVC.tableView(_:cellForRowAt:) + 90 (SomeVC.swift:90)
1 AppName 0xd500 @objc SomeVC.tableView(_:cellForRowAt:) + 4300477696 (<compiler-generated>:4300477696)
2 UIKitCore 0x285f20 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 1532
3 UIKitCore 0x483330 -[UITableView _updateVisibleCellsForRanges:createIfNecessary:] + 732
4 UIKitCore 0x2ab118 -[UITableView _updateVisibleCellsNow:] + 1432
5 UIKitCore 0x17b5a0 -[UITableView layoutSubviews] + 456
6 UIKitCore 0x18b844 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2592
7 QuartzCore 0x401c0 CA::Layer::layout_if_needed(CA::Transaction*) + 532
8 QuartzCore 0x325fc CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 136
9 QuartzCore 0x46f70 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 452
10 QuartzCore 0x4fe78 CA::Transaction::commit() + 704
11 QuartzCore 0x31d7c CA::Transaction::flush_as_runloop_observer(bool) + 88
12 UIKitCore 0x53d9d8 _UIApplicationFlushCATransaction + 72
13 UIKitCore 0x7d8084 _UIUpdateSequenceRun + 84
14 UIKitCore 0xe5dcb0 schedulerStepScheduledMainSection + 144
15 UIKitCore 0xe5d478 runloopSourceCallback + 92
16 CoreFoundation 0xbbf04 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
17 CoreFoundation 0xccc90 __CFRunLoopDoSource0 + 208
18 CoreFoundation 0x6184 __CFRunLoopDoSources0 + 268
19 CoreFoundation 0xbb4c __CFRunLoopRun + 828
20 CoreFoundation 0x1f6b8 CFRunLoopRunSpecific + 600
21 GraphicsServices 0x1374 GSEventRunModal + 164
22 UIKitCore 0x513e88 -[UIApplication _run] + 1100
23 UIKitCore 0x2955ec UIApplicationMain + 364
24 AppName 0x57e4 main + 15 (OnboardingViewModel.swift:15)
25 ??? 0x100ef9ce4 (Missing)
SomeVC
中的第 90 行指向
switch indexPath.row {
唯一让用户团结起来的是剩余多少内存,似乎是在用户内存不足时发生的:
User 1, RAM free: 45.75 MB
User 2, RAM free: 64.48 MB
User 3, RAM free: 41.34 MB
User 4, RAM free: 29.56 MB
User 5, RAM free: 61.3 MB
其他一切都不一样:设备类型、iOS版本等
此外,即使我有之前发生的分析事件的完整日志,我也找不到重现此崩溃的方法。没有什么疯狂的,他们只是像其他用户一样使用该应用程序而没有发生此崩溃。
我尝试过“模拟内存警告”选项以及通过创建一些填充函数来完全填充模拟器的 RAM,但没有任何方法可以重现这种情况。
也许有人对如何重现或修复此崩溃有想法,或者了解导致崩溃的原因?
谢谢!
更新
在以switch
开头的行提供崩溃的函数
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correctCount()...correctCount() + currentModel.files.count:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
cell.setupUI(id: currentModel.id, file: currentModel.files[indexPath.row - correctCount()], vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
您发布的信息很少,但基于这些信息,唯一可疑的代码是 correctCount
部分。
基于它正在访问数组中超出索引项的假设,在这种情况下应该可以解决该问题。但是,也有可能是 correctCount()
本身导致了崩溃,因为不知道那是什么。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
let correct_count = correctCount()
let filesCount = currentModel.files.count
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correct_count...(correct_count + filesCount):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
let fileIndex = indexPath.row - correct_count
guard fileIndex < currentModel.files.count else { return UITableViewCell() }
let file = currentModel.files[fileIndex]
cell.setupUI(id: currentModel.id, file: file, vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
如果 correctCount 和 indexPath 不同步,这将防止崩溃。但是,如果它们不同步,这可能意味着您的 table 视图在数据更改时未更新。
更好的方法可能是每次数据更改时计算 correctCount()
,并且需要更新 table 视图。然后让 table 视图代码使用该变量而不是重新计算它。
然后总是更新数据,correctCount 变量,然后 table 视图一起在同一个地方,按照那个顺序,所以他们不会首先不同步。
如果 currentModel.files
数组是动态的并且会发生变化,table 视图可能会从文件数组的本地副本加载数据,并且 ViewController 应该会监听更改文件数组并更新 table 视图数据以显示新数据。
通常,您总是希望 UITableView 之类的东西指向本地 static
数据副本,而不是真正的动态数据本身。否则,table 视图及其索引路径计算可能会与现实不同步,并开始尝试访问数组中不存在的项目。
Firebase Crashlytics 日志中有一个经常发生的奇怪崩溃,它说:
Crashed: com.apple.main-thread
0 AppName 0xd454 SomeVC.tableView(_:cellForRowAt:) + 90 (SomeVC.swift:90)
1 AppName 0xd500 @objc SomeVC.tableView(_:cellForRowAt:) + 4300477696 (<compiler-generated>:4300477696)
2 UIKitCore 0x285f20 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 1532
3 UIKitCore 0x483330 -[UITableView _updateVisibleCellsForRanges:createIfNecessary:] + 732
4 UIKitCore 0x2ab118 -[UITableView _updateVisibleCellsNow:] + 1432
5 UIKitCore 0x17b5a0 -[UITableView layoutSubviews] + 456
6 UIKitCore 0x18b844 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2592
7 QuartzCore 0x401c0 CA::Layer::layout_if_needed(CA::Transaction*) + 532
8 QuartzCore 0x325fc CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 136
9 QuartzCore 0x46f70 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 452
10 QuartzCore 0x4fe78 CA::Transaction::commit() + 704
11 QuartzCore 0x31d7c CA::Transaction::flush_as_runloop_observer(bool) + 88
12 UIKitCore 0x53d9d8 _UIApplicationFlushCATransaction + 72
13 UIKitCore 0x7d8084 _UIUpdateSequenceRun + 84
14 UIKitCore 0xe5dcb0 schedulerStepScheduledMainSection + 144
15 UIKitCore 0xe5d478 runloopSourceCallback + 92
16 CoreFoundation 0xbbf04 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
17 CoreFoundation 0xccc90 __CFRunLoopDoSource0 + 208
18 CoreFoundation 0x6184 __CFRunLoopDoSources0 + 268
19 CoreFoundation 0xbb4c __CFRunLoopRun + 828
20 CoreFoundation 0x1f6b8 CFRunLoopRunSpecific + 600
21 GraphicsServices 0x1374 GSEventRunModal + 164
22 UIKitCore 0x513e88 -[UIApplication _run] + 1100
23 UIKitCore 0x2955ec UIApplicationMain + 364
24 AppName 0x57e4 main + 15 (OnboardingViewModel.swift:15)
25 ??? 0x100ef9ce4 (Missing)
SomeVC
中的第 90 行指向
switch indexPath.row {
唯一让用户团结起来的是剩余多少内存,似乎是在用户内存不足时发生的:
User 1, RAM free: 45.75 MB
User 2, RAM free: 64.48 MB
User 3, RAM free: 41.34 MB
User 4, RAM free: 29.56 MB
User 5, RAM free: 61.3 MB
其他一切都不一样:设备类型、iOS版本等
此外,即使我有之前发生的分析事件的完整日志,我也找不到重现此崩溃的方法。没有什么疯狂的,他们只是像其他用户一样使用该应用程序而没有发生此崩溃。
我尝试过“模拟内存警告”选项以及通过创建一些填充函数来完全填充模拟器的 RAM,但没有任何方法可以重现这种情况。
也许有人对如何重现或修复此崩溃有想法,或者了解导致崩溃的原因?
谢谢!
更新
在以switch
开头的行提供崩溃的函数func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correctCount()...correctCount() + currentModel.files.count:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
cell.setupUI(id: currentModel.id, file: currentModel.files[indexPath.row - correctCount()], vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
您发布的信息很少,但基于这些信息,唯一可疑的代码是 correctCount
部分。
基于它正在访问数组中超出索引项的假设,在这种情况下应该可以解决该问题。但是,也有可能是 correctCount()
本身导致了崩溃,因为不知道那是什么。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
let correct_count = correctCount()
let filesCount = currentModel.files.count
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correct_count...(correct_count + filesCount):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
let fileIndex = indexPath.row - correct_count
guard fileIndex < currentModel.files.count else { return UITableViewCell() }
let file = currentModel.files[fileIndex]
cell.setupUI(id: currentModel.id, file: file, vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
如果 correctCount 和 indexPath 不同步,这将防止崩溃。但是,如果它们不同步,这可能意味着您的 table 视图在数据更改时未更新。
更好的方法可能是每次数据更改时计算 correctCount()
,并且需要更新 table 视图。然后让 table 视图代码使用该变量而不是重新计算它。
然后总是更新数据,correctCount 变量,然后 table 视图一起在同一个地方,按照那个顺序,所以他们不会首先不同步。
如果 currentModel.files
数组是动态的并且会发生变化,table 视图可能会从文件数组的本地副本加载数据,并且 ViewController 应该会监听更改文件数组并更新 table 视图数据以显示新数据。
通常,您总是希望 UITableView 之类的东西指向本地 static
数据副本,而不是真正的动态数据本身。否则,table 视图及其索引路径计算可能会与现实不同步,并开始尝试访问数组中不存在的项目。