Home > database >  tableView(_:cellForRowAt:) crashes with com.apple.main-thread EXC_BREAKPOINT
tableView(_:cellForRowAt:) crashes with com.apple.main-thread EXC_BREAKPOINT

Time:05-22

There is one strange crash in Firebase Crashlytics logs that occurs quite often, saying:

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)

Line 90 in SomeVC points to

switch indexPath.row {

The only thing that unites the users is how much RAM is left, seems it's happened when the user has low memory:

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

Everything else is different: device type, iOS version, etc.

Also, I can't find a way how to reproduce this crash even I have a full log of analytics events that happened before. Nothing crazy, they just using the app as the rest users without this crash.

I've tried to "Simulate memory warning" option as well fully filling the simulator's RAM by creating some fillings functions, but nothing helps to reproduce this case.

Maybe anyone has ideas on how to reproduce or fix this crash, or understand what can be the cause of that?

Thank you!

UPDATE

the function that provides the crash at the line starting with 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()
    }
}

CodePudding user response:

You have posted very little information to go on, but based on what little there is the only suspicious bit of code is the correctCount section.

Based on the assumption it is accessing an out of index item in the array this should solve it in that case. However, it is also possible it could be correctCount() itself which is causing the crash, as that is unknown what that is.


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()
    }
}

This would prevent a crash if correctCount and the indexPath's get out of sync. However, if they are getting out of sync that likely means your table view isn't being updated when the data changes.

A better approach might be to instead calculate correctCount() every time the data changes, and the table view needs to be updated. Then have the table view code use that variable instead of recalculating it.

Then always update the data, the correctCount variable, and then the table view together in the same place, in that order, so they can not get out of sync in the first place.

If the currentModel.files array is dynamic and changes, the table view should likely be loading data from a local copy of the files array, and the ViewController should be listening for changes to the file array and updating the table view data to show the new data.

In general you always want something like a UITableView to be pointing to a local static copy of data, instead of the real dynamic data itself. As otherwise the table view, and it's index path calculations, can get out of sync with reality and start trying to access items in the array which just don't exist.

  • Related