如何在insertRowsAtIndexPaths和设备轮换期间保护tableview不会崩溃

时间:2014-02-21 12:11:26

标签: ios uitableview

如果您在行动画期间不断旋转设备,此示例应用程序将在一段时间后崩溃。即使在行动画中的第一次旋转,我的真实应用程序也会崩溃。

我应该如何保护我的应用在旋转期间使用同时行动画时崩溃?在动画完成之前,请不要建议禁止轮换。 DataSource依赖于网络提取,可能需要1到30秒,具体取决于用户网络,如果用户希望在启动后立即更好地查看应用程序,则用户想要旋转设备。

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        for (int i = 0; i < 30; i++) {

            [NSThread sleepForTimeInterval:0.2]; // imitates fetching and parsing
            [self.array addObject:[NSString stringWithFormat:@"cell number %d", i]];

            dispatch_async(dispatch_get_main_queue(), ^{

                // perform on main
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
                [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            });
        }
    });
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.array.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    // Configure the cell...
    cell.textLabel.text = self.array[indexPath.row];

    return cell;
}

- (NSMutableArray *)array
{
    if (!_array) {
        _array = [[NSMutableArray alloc] init];
    }
    return _array;
}

崩溃报告

2014-02-21 12:47:24.667 RowsAnimationRotate[2062:60b] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2903.23/UITableView.m:1330
2014-02-21 12:47:24.673 RowsAnimationRotate[2062:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (8) must be equal to the number of rows contained in that section before the update (8), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

3 个答案:

答案 0 :(得分:8)

你基本上已经创造了竞争条件。

问题是你在后台线程中操作self.array,而self.tableView insertRowsAtIndexPaths正在主线程上运行,并且将访问self.array

所以在某些时候self.tableView insertRowsAtIndexPaths(或其他因为这个而调用的tableView方法)在主线程上运行,期望self.array中有一定数量的对象,但后台线程进入在那里又增加了一个...

修复模拟:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{

    for (int i = 0; i < 30; i++) {

        [NSThread sleepForTimeInterval:0.2]; // imitates fetching and parsing
        NSString *myNewObject = [NSString stringWithFormat:@"cell number %d", i]];

        dispatch_async(dispatch_get_main_queue(), ^{

            // perform on main
            [self.array addObject: myNewObject];
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
            [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        });
    }
});

答案 1 :(得分:0)

你可以改变

吗?
dispatch_async(dispatch_get_main_queue(), ^{

                // perform on main
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
                [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            });

使用:

dispatch_async(dispatch_get_main_queue(), ^{

                // perform on main
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
                [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
            });

答案 2 :(得分:0)

您可以尝试使用synchronized进行模拟: -

它声明了代码块周围的关键部分。在多线程代码中,@ synchronized保证在任何给定时间只有一个线程可以在块中执行该代码。

@synchronized(self.tableView) {
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        for (int i = 0; i < 30; i++) {

            [NSThread sleepForTimeInterval:0.2]; // imitates fetching and parsing
            [self.array addObject:[NSString stringWithFormat:@"cell number %d", i]];

            dispatch_async(dispatch_get_main_queue(), ^{

                // perform on main
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
                [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            });
        }
    });                                    
                                }

希望有所帮助。如果我们必须采用其他解决方案,请告诉我。