Block_release освобождает объекты пользовательского интерфейса в фоновом потоке - PullRequest
17 голосов
/ 15 июня 2011

Одним из шаблонов, представленных в докладе WWDC 2010 «Блоки и Grand Central Dispatch», было использование вложенных вызовов 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», он автоматически получает «retain» и позже получает «release» после очистки блоков.

Если вызов 'release' блока является последним вызовом release (например, пользователь уходит от представления во время выполнения фоновой задачи), вызывается метод dealloc myViewController - но он вызывается в фоновом потоке !!

Объекты UIKit не любят, когда их выделяют за пределами основного потока. В моем случае UIWebView выдает исключение.

Как этот шаблон, представленный WWDC, специально упомянутый как лучший новый способ избежать блокировки пользовательского интерфейса, может быть настолько ошибочным? Я что-то пропустил?

Ответы [ 3 ]

11 голосов
/ 30 июня 2011

Вы можете использовать квалификатор типа хранения __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's 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's released on main thread
    }
});
2 голосов
/ 30 октября 2012

В потоке я просто использую [viewController retain];, затем в конце потока используйте [viewController release].Это работает, и я не использую GCD~

0 голосов
/ 26 июня 2011

Это сработало для меня (добавил таймер):

[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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...