快速行插入UITableView会导致NSInternalInconsistencyException

时间:2013-07-16 00:03:18

标签: ios uitableview nsnotificationcenter

我有一个UITableView,有时会快速插入新行。新行的插入由通知观察者处理,该通知观察者在基础数据发生变化时监听更新通知。我在所有数据模型更改和实际通知发布后使用@synchronized块...希望每个增量数据更改(和行插入)将单独处理。但是,有时候这仍然会失败。该异常将告诉我它预计有10行(基于来自数据模型的计数),它之前有8行,但更新通知只告诉它插入一行(因为这是两个快速触发的通知中的第一行) )。

我试图了解其他人如何处理这些类型的情况。其他开发人员如何缓解两个表视图更新操作之间存在多线程竞争条件的问题?我是否应该有一个更安全的锁来控制更新通知(为什么@synchronized不执行它应该做的事情)?

非常感谢任何建议!感谢。

一些伪代码:

我的模型类有一个像这样的方法,其他线程调用它来将新行追加到表视图中:

- (void)addRow:(NSString *)data
{
    @synchronized(self.arrayOfData)
    {
        NSInteger nextIndex = self.arrayData.count;
        [self.arrayData addObject:data];
        [NSNotificationCenter.defaultCenter postNotificationName:kDataUpdatedNotification object:self userInfo:@{@"insert": @[[NSIndexPath indexPathForRow:nextIndex inSection:0]]}];
    }
}

我的控制器类有一个这样的方法来接受kDataUpdatedNotification通知并实际执行行插入:

- (void)onDataUpdatedNotification:(NSNotification *)notification
{
    NSDictionary *changes = notification.userInfo;
    [self.tableView insertRowsAtIndexPaths:changes[@"insert"] withRowAnimation:UITableViewRowAnimationBottom];
} 

1 个答案:

答案 0 :(得分:8)

如果您与主队列异步更改数据模型,则会出现此问题,因为表视图委托方法正在查看数据模型的当前状态,这可能位于您报告的插入之前到表视图。

<强>更新

一种解决方案是将更新排队到专用队列上,让该队列同步更新主队列上的数据模型(我还没有测试过这段代码):

@interface MyModelClass ()
@property (strong, nonatomic) dispatch_queue_t myDispatchQueue;
@end

@implementation MyModelClass

- (dispatch_queue_t)myDispatchQueue
{
    if (_myDispatchQueue == nil) {
        _myDispatchQueue = dispatch_queue_create("myDispatchQueue", NULL);
    }
    return _myDispatchQueue;
}

- (void)addRow:(NSString *)data
{
    dispatch_async(self.myDispatchQueue, ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSInteger nextIndex = self.arrayData.count;
            [self.arrayData addObject:data];
            [NSNotificationCenter.defaultCenter postNotificationName:kDataUpdatedNotification object:self userInfo:@{@"insert": @[[NSIndexPath indexPathForRow:nextIndex inSection:0]]}];
        });
    });
}

您需要中间调度队列的原因如下。在原始解决方案(下面)中,您将在主队列中获得一系列看起来像这样的块:

  1. 添加行N
  2. 添加第N + 1行
  3. 由表格视图发布的第N行动画
  4. 按行N + 1动画的表格视图发布的块
  5. 在步骤(3)中,动画块与表视图不同步,因为(2)首先发生,这导致异常(我认为断言失败)。因此,通过从私有调度队列同步将添加行块发布到主队列,您将获得如下内容:

    1. 添加行N
    2. 由表格视图发布的第N行动画
    3. 添加第N + 1行
    4. 按行N + 1动画的表格视图发布的块
    5. 没有阻止你的工作人员队列。

      ORIGINAL 解决方案仍存在重叠动画的问题。

      如果您更新主队列上的数据模型,我认为您会没事的:

      - (void)addRow:(NSString *)data
      {
          dispatch_async(dispatch_get_main_queue(), ^{
              NSInteger nextIndex = self.arrayData.count;
              [self.arrayData addObject:data];
              [NSNotificationCenter.defaultCenter postNotificationName:kDataUpdatedNotification object:self userInfo:@{@"insert": @[[NSIndexPath indexPathForRow:nextIndex inSection:0]]}];
          });
      }