4

我有一个 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];
} 
4

1 回答 1

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 行动画块

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

  1. 添加第 N 行
  2. 表格视图发布的第 N 行动画块
  3. 添加第 N+1 行
  4. 表格视图发布的第 N+1 行动画块

无需占用您的工作队列。

原始解决方案仍然存在重叠动画的问题。

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

- (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]]}];
    });
}
于 2013-07-16T00:39:42.553 回答