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 视图及其索引路径计算可能会与现实不同步,并开始尝试访问数组中不存在的项目。