UIWebView в многопоточном ViewController - PullRequest
9 голосов
/ 03 июня 2009

У меня есть UIWebView в viewcontroller, который имеет два метода, как показано ниже. Вопрос в том, что если я выскочу (коснусь назад на панели навигации) этого контроллера до того, как будет завершен второй поток, приложение завершит работу после [super dealloc], потому что «Попытка получить веб-блокировку из потока, отличного от основного потока или веб-поток. Это может быть результатом обращения к UIKit из вторичного потока. " Любая помощь будет очень признательна.

-(void)viewDidAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(load) object:nil];
    [operationQueue addOperation:operation];
    [operation release];
}

-(void)load {
    [NSThread sleepForTimeInterval:5];
    [self performSelectorOnMainThread:@selector(done) withObject:nil waitUntilDone:NO];
}

Ответы [ 8 ]

35 голосов
/ 28 ноября 2010

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

Вышеуказанное [[self retain] autorelease] все равно приведет к окончательному выпуску из пула автоматического выпуска фонового потока. (Если только в выпусках из пула авто-релизов нет ничего особенного, я удивляюсь, что это изменит).

Я нашел это как идеальное решение, поместив этот код в мой класс контроллера представления:

- (oneway void)release
{
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
    } else {
        [super release];
    }
}

Это гарантирует, что метод release моего класса контроллера представления всегда выполняется в главном потоке.

Я немного удивлен, что некоторые объекты, которые могут быть правильно удалены только из основного потока, уже не имеют чего-то подобного. О, хорошо ...

12 голосов
/ 13 апреля 2012

Вот некоторый код для запуска элементов UIKit в главном потоке. Если вы работаете в другом потоке и вам нужно запустить фрагмент кода UIKit, просто поместите его в скобки этого фрагмента Grand Central Dispatch.

dispatch_async(dispatch_get_main_queue(), ^{

    // do work here

});
1 голос
/ 27 сентября 2010

Я пытался:

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

, который, кажется, работает еще лучше.

1 голос
/ 31 июля 2009

У меня сейчас похожая проблема в моем приложении. Контроллер представления, который отображает UIWebView, передается на контроллер навигации и запускает фоновый поток для извлечения данных. Если вы нажмете кнопку «Назад» до завершения потока, приложение завершит работу с тем же сообщением об ошибке.

Проблема, похоже, в том, что NSThread сохраняет цель (себя) и объект (аргумент) и освобождает их после запуска метода - к сожалению, он освобождает оба из потока. Поэтому, когда контроллер создается, счет сохранения равен 1, при запуске потока контроллер получает счет сохранения 2. Когда вы выталкиваете контроллер до завершения потока, контроллер навигации освобождает контроллер, что приводит к сохранить количество 1. Пока это нормально - но если поток, наконец, завершается, NSThread освобождает контроллер, что приводит к сохранению счетчика 0 и немедленному освобождению изнутри потока. Это приводит к тому, что UIWebView (который выпускается в методе dealloc контроллера) вызывает это предупреждение об ошибке потока и вылетает.

Я успешно обошел эту проблему, используя [[self retain] autorelease] в качестве последнего оператора в потоке (непосредственно перед тем, как поток освободит свой пул). Это гарантирует, что объект контроллера не освобождается сразу, а помечается как автоматически освобожденный и освобождается позже в цикле выполнения основного потока. Однако это довольно грязный хак, и я бы предпочел найти лучшее решение.

1 голос
/ 03 июня 2009

В общем, вы должны отменить любые фоновые операции, когда представление, которое их использует, исчезает. Как в:

- (void)viewWillDisappear:(BOOL)animated {
  [operationQueue cancelAllOperations];
  [super viewWillDisappear:animated;
}
0 голосов
/ 13 сентября 2012
- (void)dealloc
{
    if(![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(dealloc) 
                               withObject:nil 
                            waitUntilDone:[NSThread isMainThread]];
        return;
    } 
    [super dealloc];
}
0 голосов
/ 11 сентября 2009

Я пробовал оба решения, опубликованные выше, [operationQueue cancelAllOperations] и [[self retain] autorelease]. Тем не менее, при быстром щелчке все еще есть случаи, когда количество сохранений падает до 0 и класс освобождается во вторичном потоке. Чтобы избежать сбоев, я добавил в свой dealloc следующее:

    if ([NSThread isMainThread]) {
        [super dealloc];
    }

, которая является очевидной утечкой, но кажется меньшей из 2 зол.

Любая дополнительная информация от тех, кто сталкивается с этой проблемой, приветствуется.

0 голосов
/ 04 июня 2009

Я не уверен точно, что происходит на основе вашего кода, но похоже, что viewDidAppear вызывается и создается второй поток, а затем вы уходите от контроллера и освобождаете его, а затем второй поток завершается и вызывает executeSelectorOnMainThread для освобожденного объекта «self». Думаю, вам просто нужно проверить, что релиз не произошел?

Сообщение об ошибке, которое вы получаете, подразумевает, что вы запускаете некоторый код UIKit из вашего второго потока. Apple недавно добавила некоторые проверки для многопоточных вызовов в UIKit, и я думаю, что вам, вероятно, просто нужно реорганизовать свою функцию загрузки для обновления пользовательского интерфейса в главном потоке, вместо вызова функций UIWebView из второго потока.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...