关于UITableView刷新方法beginUpdates/endUpdates的坑

926 阅读1分钟

背景

最近做了个需求,UITableView用了beginUpdates/endUpdates来刷新单个cell,但是发现左滑删除数据时会出现数组越界的崩溃,模拟代码如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    __block NSIndexPath *index = indexPath;
    __weak __typeof(&*self)weakSelf = self;
    cell.deleteBlock = ^(void) {
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong __typeof(&*weakSelf)self = weakSelf;
            NSIndexPath *path = [NSIndexPath indexPathForRow:index inSection:0];
            [self.dataSource removeObject:obj];
            [self.tableview beginUpdates];
            [self.tableview deleteRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft];
            [self.tableview endUpdates];
        });
    };
 }
    

原因

经过定位问题,最终发现是beginUpdates/endUpdates的问题,用这个方法做刷新是比直接reloadData在性能上更好,因为reloadData是刷新整个tableview的,而beginUpdates/endUpdates只会更新单个cell,但是在删除代码时,因为只更新了单个cell,其他cell不会改变,其对应的index也不会因为删除了cell而变更,导致删除更大的行数时超出了数据源的个数而出现数组越界了。

解决

因为不想reloadData刷新整个tableview,最后用改成用选中的cell对应的obj去数据源寻找真正的index。 模拟代码如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSIndexPath *index = indexPath;
    __block NSString *obj = [self.dataSource objectAtIndex:index];
    cell.obj = obj;
    __weak __typeof(&*self)weakSelf = self;
    cell.deleteBlock = ^(NSString * _Nonnull obj) {
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong __typeof(&*weakSelf)self = weakSelf;
           NSInteger index = [self.dataSource indexOfObject:obj];
            if (index != NSNotFound) {
                NSIndexPath *path = [NSIndexPath indexPathForRow:index inSection:0];
                [self.dataSource removeObject:obj];
                [self.tableview beginUpdates];
                [self.tableview deleteRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft];
                [self.tableview endUpdates];
            }
        });
    };
 }