Проблема с асинхронной загрузкой UITableView

#objective-c #asynchronous #uitableview #nsurlconnection #nsurlrequest

#objective-c #асинхронный #uitableview #nsurlconnection #nsurlrequest

Вопрос:

Я пытаюсь асинхронно загружать изображения для UITableViewCell, но в данный момент для каждой ячейки устанавливается одно и то же изображение.

Пожалуйста, не могли бы вы рассказать мне о проблеме с моим кодом:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }

    SearchObject *so = (SearchObject *)[_tableData objectAtIndex:indexPath.row];
    cell.textLabel.text = [[[[so tweet] stringByReplacingOccurrencesOfString:@"amp;quot;" withString:@"""] stringByReplacingOccurrencesOfString:@"amp;<" withString:@"<"] stringByReplacingOccurrencesOfString:@"amp;>" withString:@">"];
    cell.detailTextLabel.text = [so fromUser];
    if (cell.imageView.image == nil) {
        NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:[so userProfileImageURL]]];
        NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
        [conn start];
    }
    if ([_cellImages count] > indexPath.row) {
        cell.imageView.image = [UIImage imageWithData:[_cellImages objectAtIndex:indexPath.row]];
    }
    return cell;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_cellData appendData:data];
    [_cellImages addObject:_cellData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.tableView reloadData];
}
  

Ответ №1:

Вы добавляете данные из каждого загруженного изображения в один и тот же объект данных. Таким образом, в лучшем случае объект данных заканчивается данными для изображения # 1, за которыми сразу следуют данные для изображения # 2 и так далее; декодер изображения, по-видимому, берет первое изображение в блоке данных и игнорирует мусор после. Вы также, похоже, не знаете, что NSURLConnections’ connection:didReceiveData: не обязательно будет вызываться в том порядке, в котором были запущены соединения, это connection:didReceiveData: может быть вызвано ноль или несколько раз для каждого соединения (и, вероятно, будет, если ваши изображения занимают более нескольких кибибайт), и это tableView:cellForRowAtIndexPath: не гарантируется для вызова каждой ячейки в таблице по порядку. Все это собирается полностью испортить ваш _cellImages массив.

Чтобы сделать это правильно, вам нужно иметь отдельный экземпляр NSMutableData для каждого соединения, и вам нужно добавить его в ваш _cellImages массив только один раз, и с правильным индексом для строки, а не с произвольным следующим доступным индексом. И затем в connection:didReceiveData: вам нужно определить правильный экземпляр NSMutableData для добавления; это может быть сделано с помощью объекта connection (завернутого в NSValue с использованием valueWithNonretainedObject: ) в качестве ключа в NSMutableDictionary, или с помощью objc_setAssociatedObject прикрепления объекта data к объекту connection, или путем создания собственного класса, который обрабатывает все управление NSURLConnection для вас и передает вам объект data после завершения.

Ответ №2:

Я не знаю, является ли это причиной проблемы или нет, но в вашем connection:didReceiveData: методе вы просто добавляете данные изображения в массив; вы должны хранить данные изображения таким образом, чтобы вы могли связать их с ячейкой, в которой они должны отображаться. Одним из способов сделать это было бы использовать an, NSMutableArray заполненный множеством [NSNull] s, затем заменить null значение на соответствующий индекс, когда соединение завершит загрузку.

Кроме того, вы добавляете _cellData в _cellImages массив, когда загрузка соединения не завершена, вы должны делать это только в connection:didFinishLoading методе.