iPhone: проблемы с выпуском UIViewController в многопоточной среде - PullRequest
0 голосов
/ 30 мая 2010

У меня есть UIViewController, и в этом контроллере я получаю изображение из источника URL. Изображение извлекается в отдельном потоке, после чего пользовательский интерфейс обновляется в основном потоке. Этот контроллер отображается как страница в родительском объекте UIScrollView, который реализован для освобождения контроллеров, которые больше не отображаются.

Когда поток завершает выборку содержимого до выпуска UIViewController, все работает нормально - но когда пользователь переходит на другую страницу до завершения потока, контроллер освобождается, и единственный дескриптор контроллера принадлежит потоку, создающему releaseCount контроллера равно 1. Теперь, как только поток истощает NSAutoreleasePool, контроллер получает релизы, потому что releaseCount становится равным 0. В этот момент мое приложение падает, и я получаю следующее сообщение об ошибке:

bool _WebTryThreadLock (bool), 0x4d99c60: Пытался получить веб-блокировку из потока, отличного от основного или веб-потока. Это может быть результатом обращения к UIKit из вторичного потока. Авария сейчас ...

Обратная трассировка показывает, что приложение вызвало сбой при вызове [super dealloc], и это имеет смысл, так как функция dealloc должна была запускаться потоком, когда пул очищался. У меня вопрос, как я могу преодолеть эту ошибку и освободить контроллер без утечки памяти?

Одним из решений, которое я попытался, было вызвать [self retain] до опустошения пула, чтобы retainCount не упал до нуля, а затем использовать следующий код для освобождения контроллера в главном потоке:

[self performSelectorOnMainThread:@selector(autorelease) 
     withObject:nil waitUntilDone:NO];

К сожалению, это не сработало. Ниже приведена функция, выполняемая в потоке:

- (void)thread_fetchContent {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSURL *imgURL = [NSURL URLWithString:@"http://www.domain.com/image.png"];

    // UIImage *imgHotspot is declared as private - The image is retained 
    // here and released as soon as it is assigned to UIImageView

    imgHotspot = [[[UIImage alloc] initWithData:
         [NSData dataWithContentsOfURL: imgURL]] retain];

    if ([self retainCount] == 1) {

        [self retain]; // increment retain count ~ workaround
        [pool drain]; // drain pool

        // this doesn't work - i get the same error

        [self performSelectorOnMainThread:@selector(autorelease)
             withObject:nil waitUntilDone:NO];
    }

    else {

        // show fetched image on the main thread - this works fine!

        [self performSelectorOnMainThread:@selector(showImage)
             withObject:nil waitUntilDone:NO];

        [pool drain];
    }
}

Пожалуйста, помогите! Заранее спасибо.

1 Ответ

0 голосов
/ 30 мая 2010

Да, это может быть очень сложно, пытаться синхронизировать потоки. Описанный вами сценарий использования идеально подходит для NSOperation. Используя этот подход, вы можете использовать NSOperationQueue в качестве ivar в контроллере и разблокировать его в методе dealloc ваших контроллеров.

Преимущества много, когда представление контроллеров видно в scrollView, оно (viewWillAppear или loadView) начинает извлекать изображение, используя NSOperation, добавленный в NSOperationQueue, если пользователь затем прокручивает страницу до выполнения операции и NSOperationQueue после освобождения он позаботится об отправке сообщения об отмене всем операциям и, в общем, закроет все по порядку.

Если это центральный компонент в вашем приложении, что, я думаю, так и есть, поскольку вы задумывались о выпуске «экранных» вещей, я бы порекомендовал вашему контроллеру отобразить «фиктивное изображение» в методе loadVIew, а затем начать операцию выборки в viewDidLoad. Вы можете создать подкласс NSOperation, так что вы просто отправляете ему URL и позволяете ему делать свое дело.

Несколько недель назад я делал нечто подобное, когда мне приходилось непрерывно запускать многопоточные операции, но с большой вероятностью пользователь делал что-то, что приводило к их отмене. Эта функциональность «встроена» в NSOperation. NSOperation вопрос

...