Как мы синхронизируем numberOfRowsInSection с cellForRowAtIndexPath? - PullRequest
3 голосов
/ 19 июля 2011

Наш UITableView возвращает количество строк для данного раздела.То, что мы испытываем к моменту вызова cellForRowAtIndexPath, число изменилось, и мы в итоге получаем индекс массива вне границ.

Есть ли хороший способ синхронизации этих двух методов, чтобы основные данные не изменялись?Мы рассмотрели возможность использования @synchronized, но не знали, когда вы снимите блокировку.

Еще одна вещь, которую мы делаем для обновления таблицы, это из отдельного потока.

[self addUsers:usersToShow];       
[[self invokeOnMainThreadAndWaitUntilDone:NO] refresh:self]; // is this the issue?


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.users.count;  // returns 10 for example
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *newCell= nil;

    static NSString *cellId = @"cellId";

    cell = [tableView dequeueReusableCellWithIdentifier:cellId];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:self options:nil];
        cell = newCell;
        self.newCell = nil;
    }

    User* user = [self.users objectAtIndex:indexPath.row]; // index out of bounds now because number of users has changed
}

Ответы [ 2 ]

2 голосов
/ 06 мая 2015

Несмотря на то, что мозг уже ответил, я хочу подчеркнуть «обновить модель в главном потоке» с примером кода.

Вы можете столкнуться с проблемой, потому что ваша модель изменилась в некотором фоновом потоке. Временная шкала должна выглядеть следующим образом:

{NSThread number = 1, name = main} - [ViewController tableView: numberOfRowsInSection:] (например, вернуть 10)

{NSThread number = 8, name = (null)} - [ViewController changeTheModel] (удалить некоторые объекты из модели или получить новую модель с менее чем 10 объектами)

{NSThread number = 1, name = main} - [ViewController tableView: cellForRowAtIndexPath:] (получить индекс из связанной исключительной ситуации, поскольку 10-й объект не существует)

Чтобы решить эту проблему, вы должны сделать что-то подобное при смене модели:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSArray* items = [self getNewModel];// get new model on background thread
    dispatch_async(dispatch_get_main_queue(), ^ {
        self.items = items;// replace the model with new one on main thread
        [self.tableView reloadData];// refresh table without index out of bound exception
    });
});

Надеюсь, это поможет вам. :)

2 голосов
/ 19 июля 2011

Поскольку вы отработали для себя, вы не можете использовать @synchronized, потому что область действия выходит за рамки метода.

Вы не хотите пытаться использовать объект Lock, блокируя его в numberOfRowsInSection и разблокируя его cellForRowAtIndexPath. Это не сработает. Что вам нужно сделать, так это убедиться, что вы делаете любую блокировку, которая вам нужна в cellForRowAtIndexPath, и обрабатываете тот факт, что переданная строка может быть недействительной, например ::

User * user = nil;    
@synchronized(self.users) {
        if (indexPath.row < [self.users count]) {
             user = [self.users objectAtIndex:indexPath.row];
        }

}

if (user) {
    //configure cell
}
else {
    //set cell fields to be blank
}

Вы пытались обновить модель (self.users) только в главном потоке? Это должно уменьшить вероятность того, что ваши обновления модели будут чередоваться с вызовами getNumberOfRows и configureCellAt. В приведенном вами примере вы обновляете модель в случайном потоке, а затем перезагружаете данные в основном потоке. Я бы порекомендовал убедиться, что ваша модель является поточно-ориентированной (или обновлена ​​/ доступна только в основной ветке).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...