4

新しい行がすばやく挿入されることがある UITableView があります。新しい行の挿入は、基になるデータが変更されるたびに発生する更新通知をリッスンする通知オブザーバーによって処理されます。すべてのデータ モデルの変更と実際の通知投稿自体に @synchronized ブロックを使用しています...各増分データ変更 (および行挿入) が個別に処理されることを期待しています。ただし、これでも失敗する場合があります。例外は、(データ モデルからのカウントに基づいて) 10 行を期待していることを教えてくれます。以前は 8 行でしたが、更新通知は 1 行を挿入するように指示しただけです (これは、2 つの迅速に開始された通知の最初のものであるため)。 )。

私は、他の人々がこの種の状況にどのように対処する傾向があるかを理解しようとしています. 他の開発者は、2 つのテーブル ビュー更新操作の間でマルチスレッドの競合状態が発生するという問題をどのように軽減していますか? 更新通知を制御するより安全なロックが必要ですか?

どんなアドバイスでも大歓迎です!ありがとう。

いくつかの擬似コード:

私のモデル クラスには、テーブル ビューに新しい行を追加するために他のスレッドによって呼び出される次のようなメソッドがあります。

- (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

テーブルビューデリゲートメソッドがデータモデルの現在の状態を調べているため、メインキューと非同期にデータモデルを変更すると、この問題が発生します。これは、テーブルに報告した挿入よりも進んでいる可能性があります。見る。

アップデート

1 つの解決策は、プライベート キューで更新をキューに入れ、そのキューでメイン キューのデータ モデルを同期的に更新することです (このコードはテストしていません)。

@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 に答える