Block_release освобождение объектов пользовательского интерфейса в фоновом потоке
Один из шаблонов, представленных на конференции WWDC 2010 "Блоки и грандиозная центральная диспетчерская связь", заключался в использовании вложенных вызовов dispatch_async для выполнения трудоемких задач в фоновом потоке и затем обновления пользовательского интерфейса в основном потоке после завершения задачи
dispatch_async(backgroundQueue, ^{
// do something time consuming in background
NSArray *results = ComputeBigKnarlyThingThatWouldBlockForAWhile();
// use results on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[myViewController UpdateUiWithResults:results];
});
});
Так как "myViewController" используется внутри блоков, он автоматически получает "сохранить" и позже получит "release", когда блоки будут очищены.
Если вызов блока "release" является окончательным вызовом освобождения (например, пользователь перемещается в сторону от представления во время выполнения фоновой задачи) вызывается метод dealloc myViewController, но он вызывает фоновый поток!
Объекты UIKit не любят быть выделенными вне основного потока. В моем случае UIWebView выдает исключение.
Как этот WWDC может представить шаблон, который конкретно упоминается как лучший новый способ избежать блокировки пользовательского интерфейса - быть настолько ошибочным? Я что-то упускаю?
Ответы
Ответ 1
Вы можете использовать квалификатор типа __block
для такого случая. __block
переменные автоматически не сохраняются блоком. Поэтому вам нужно сохранить объект самостоятельно:
__block UIViewController *viewController = [myViewController retain];
dispatch_async(backgroundQueue, ^{
// Do long-running work here.
dispatch_async(dispatch_get_main_queue(), ^{
[viewController updateUIWithResults:results];
[viewController release]; // Ensure it released on main thread
}
});
ИЗМЕНИТЬ
С ARC объект переменной __block автоматически сохраняется блоком, но мы можем установить значение nil переменной __block для отпускания сохраненного объекта всякий раз, когда мы хотим.
__block UIViewController *viewController = myViewController;
dispatch_async(backgroundQueue, ^{
// Do long-running work here.
dispatch_async(dispatch_get_main_queue(), ^{
[viewController updateUIWithResults:results];
viewController = nil; // Ensure it released on main thread
}
});
Ответ 2
В потоке я просто использую [viewController retain];
, а затем в конце использования потока
[viewController release]
. Он работает, и я не использую GCD~
Ответ 3
Это сработало для меня (добавлен таймер):
[self retain]; // this guarantees that the last release will be on the main threaad
dispatch_async(backgroundQueue, ^{
// do something time consuming in background
NSArray *results = ComputeBigKnarlyThingThatWouldBlockForAWhile();
// use results on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[myViewController UpdateUiWithResults:results];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(releaseMe:) userInfo:nil repeats:NO];
});
});
- (void)releaseMe:(NSTimer *)theTimer {
[self release]; // will be on the main thread
}