Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView crashes on iOS 9 when endUpdates is called with EXC_BAD_ACCESS

After users upgraded to iOS 9, we've noticed a series of Bad Access (EXC_BAD_ACCESS) crashes that don't appear for users who are still on iOS 8. It happens when we call endUpdates on UITableView.

The crash logs include the following reasons:

Selector name found in current argument registers: numberOfRowsInSection:

Selector name found in current argument registers: indexPathForRowAtGlobalRow:

Stack trace #1:

1   UIKit   __46-[UITableView _updateWithItems:updateSupport:]_block_invoke + 92
2   UIKit   __46-[UITableView _updateWithItems:updateSupport:]_block_invoke1007 + 224
3   UIKit   -[UITableView _updateWithItems:updateSupport:] + 2556
4   UIKit   -[UITableView _endCellAnimationsWithContext:] + 12892
[...]

Stack trace #2:

1   UIKit   __46-[UITableView _updateWithItems:updateSupport:]_block_invoke + 100
2   UIKit   -[UITableViewRowData globalRowForRowAtIndexPath:] + 102
3   UIKit   __46-[UITableView _updateWithItems:updateSupport:]_block_invoke1007 + 182
4   UIKit   -[UITableView _updateWithItems:updateSupport:] + 2300
5   UIKit   -[UITableView _endCellAnimationsWithContext:] + 10552

We are able to repro the issue, but don't have any clue on how to go about fixing it.

like image 254
Toland Hon Avatar asked Dec 23 '15 20:12

Toland Hon


2 Answers

It looks like there's a bug in iOS9 when your UITableView has no rows that causes endUpdates to crash with EXC_BAD_ACCESS. To work around this bug, you have to call tableView reloadData before calling beginUpdates.

From the thread that Claudio Redi directed me to: iOS9 iPad UITableView Crash (EXC_BAD_ACCESS) on 1st section insert, I've implemented the following workaround you add before calling [tableView beginUpdates];

if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion >= 9)
{
    // there's a bug in iOS9 when your UITableView has no rows that causes endUpdates to crash with EXC_BAD_ACCESS
    // to work around this bug, you have to call tableView reloadData before calling beginUpdates.

    BOOL shouldReloadData = YES;
    NSInteger numberOfSections = [tableView.dataSource numberOfSectionsInTableView:tableView];
    for (NSInteger section = 0; section < numberOfSections; section++)
    {
        if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] > 0)
        {
            // found a row in current section, do not need to reload data
            shouldReloadData = NO;
            break;
        }
    }

    if (shouldReloadData) 
    {
        [tableView reloadData];
    }
}
like image 143
Toland Hon Avatar answered Sep 21 '22 12:09

Toland Hon


I faced this issue as well, the answer above of calling reloadData() on the table view did resolve the issue, however this was not ideal, as the animation associated with the update was not fluid, due to the reload.

The root of the issue is that I had programmatically called selectRow(at indexPath: IndexPath?, animated: Bool, scrollPosition: UITableView.ScrollPosition) on the table view, for an invalid index path at the time the method was called. (The updates in the table view were to expand/collapse a section to show the rows within or zero rows, I would at times select a row in a collapsed section). This behaviour did not cause a crash when selecting a row for an index path that was not visible in the table view, however when updating the UITableView between beginUpdates() and endUpdates() calls after selecting an invalid index path, there would be a crash with EXC_BAD_ACCESS at the endUpdates() call. Adding a check before calling selectRowAt: to ensure the index path was visible/valid, before programmatically selecting resolved the crash without the need to call reloadData()

like image 36
user3847320 Avatar answered Sep 20 '22 12:09

user3847320