快速行插入UITableView会导致NSInternalInconsistencyException [英] Rapid row insertion into UITableView causes NSInternalInconsistencyException

查看:122
本文介绍了快速行插入UITableView会导致NSInternalInconsistencyException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a UITableView that sometimes has rapid insertions of new rows. The insertion of the new rows is handled by a notification observer listening for the update notification fired whenever the underlying data changes. I use a @synchronized block around all the data model changes and the actual notification post itself... hoping that each incremental data change (and row insertion) will be handled separately. However, there are times when this still fails. The exception will tell me that it expects 10 rows (based on the count from the data model), it previously had 8 rows, but the update notification only told it to insert a single row (as this is the first of two rapidly fired notifications).

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

I'm trying to understand how other people tend to handle these types of situations. How do other developers mitigate the problems of having multi-threaded race conditions between two table view update operations? Should I have a more secure lock that controls the update notifications (and why isn't @synchronized doing what it's supposed to)?

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

Any advice is greatly appreciated! Thanks.

一些伪代码:

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

My model class has a method like this, which gets called by other threads to append new rows to the table view:

- (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通知并实际执行行插入:

My controller class has a method like this to accept the kDataUpdatedNotification notification and actually perform the row insertion:

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


推荐答案

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

You're going to have this problem if you change your data model asynchronously with the main queue because your table view delegate methods are looking at the current state of the data model, which may be ahead of the inserts you've reported to the table view.

更新

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

One solution is to queue your updates on a private queue and have that queue update your data model on the main queue synchronously (I have not tested this code):

@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]]}];
        });
    });
}

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

The reason you need the intermediate dispatch queue is as follows. In the original solution (below), you get a series of blocks on the main queue that look something like this:


  1. 添加行N

  2. 添加行N + 1

  3. 按行N动画的表格视图发布的块

  4. 按表发布的块行N + 1动画的视图

  1. Add row N
  2. Add row N+1
  3. Block posted by table view for row N animation
  4. Block posted by table view for row N+1 animation

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

In step (3), the animation block is out-of-sync with the table view because (2) happened first, which results in an exception (assertion failure, I think). So, by posting the add row blocks to the main queue synchronously from a private dispatch queue, you get something like the following:


  1. 添加行N

  2. 按行N动画的表格视图发布的块

  3. 添加行N + 1

  4. 阻止发布通过表格查看行N + 1动画

  1. Add row N
  2. Block posted by table view for row N animation
  3. Add row N+1
  4. Block posted by table view for row N+1 animation

而不占用你的工作人员队列。

without holding up your worker queues.

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

ORIGINAL Solution still has issues with overlapping animations.

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

I think you'll be fine if you update your data model on the main queue:

- (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]]}];
    });
}

这篇关于快速行插入UITableView会导致NSInternalInconsistencyException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆