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.
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];
}
}
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With