Обновление пользовательского интерфейса для dispatch_get_main_queue () - PullRequest
9 голосов
/ 02 февраля 2012

У меня есть один вопрос, связанный с обновлением пользовательского интерфейса в основном потоке с использованием очередей.

Хорошо, предположим, что мы создали UITableView, который показывает UILabel с UIImageView. UIImage загружаются асинхронно в prepareCellfor .. используя:

 dispatch_async(t_queue, ^{
   //load image
   //dispatch_async(dispatch_get_main_queue(),^{
     cell.imageView = image;
   }
 });

Но пока блок извлекает изображение, пользователь нажимает одну ячейку (или кнопку «Назад» на контроллере навигационного представления) для загрузки детализирующего контроллера для этой ячейки (или возвращается в приложение).

Мой вопрос: что происходит, когда блок запускает основной поток для обновления imageView для ячейки? он пытается обновить UIView, который не загружен в окно или даже может быть выгружен ...

Спасибо

1 Ответ

14 голосов
/ 02 февраля 2012

Это хороший вопрос. И ответ при использовании ARC заключается в том, что сам блок сохраняет объект, так что он будет примерно позже. Это одна из тех тонких ошибок памяти. Если UITableView, из которого поступает эта ячейка, освобождается и освобождает все ее ячейки, эта ячейка будет сохранена (хотя и за пределами экрана) и будет иметь присвоение cell.imageView = image;, тогда она будет освобождена.

Я большой поклонник контролируемых экспериментов и намеревался проверить это, но у UITableView есть много движущихся частей (не каламбур). Поэтому я создал очень простой эксперимент, используя простой подкласс NSObject следующим образом:

@implementation SayHello
-(void)sayHello{
    NSLog(@"Hello");
}
-(void)dealloc{
    NSLog(@"SayHello dead");
}
@end

Очевидно, этот класс предназначен для того, чтобы дать мне функцию для вызова в блоке (sayHello) и будет производить NSLog при отмене выделения.

Я провел свой тест так:

SayHello *hello = [[SayHello alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    double delayInSeconds = 30.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [hello sayHello];
    });
});

30 секунд дают даже самое ленивое время цикла выполнения, чтобы освободить «привет» объект (если, на самом деле, он не был сохранен). Но в течение этих 30 секунд консоль молчит. По истечении 30 секунд я немедленно получаю сообщение «Hello», а затем сообщение «SayHello dead».

Так как же это "готча"? Что ж, очевидно, если вы не понимаете, что Blocks / ARC делают это, это может закончиться тем, что вы не захотите. Но также с вашим примером UITableViewCell; что если ваша ячейка отображается один раз и отправляет запрос по сети на изображение, но пока блок ожидает изображение, ячейка используется повторно? Теперь есть второй блок со ссылкой на эту ячейку, пытающийся установить ее изображение. Что ж, теперь у вас есть гонка, в которой проигравший будет решать, какое изображение будет отображаться.

...