Хобрук: Ваш путь к мастерству в программировании

UIWebView в многопоточном ViewController

У меня есть UIWebView в контроллере представления, который имеет два метода, как показано ниже. Вопрос в том, если я вытащу (коснитесь назад на панели навигации) этот контроллер до того, как будет выполнен второй поток, приложение выйдет из строя после [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];
}


Ответы:


1

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

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

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

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

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

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

28.11.2010
  • Это закончилось большой головной болью для меня. Спасибо! 16.01.2011
  • Рад, что нашел это - апрель 2011 16.04.2011
  • Большое спасибо. Вы сэкономили мое время 12.05.2011
  • Могу ли я изменить self performSelectorOnMainThread: на super performSelectorOnMainThread:? 21.06.2011
  • Конечно. Переход на супер имеет смысл - хороший улов! 25.08.2011
  • Почему бы не переключиться на основной поток в Dealloc, а не в Release? 10.09.2011
  • Это тоже стоит попробовать. Я полагал, что любой вызов dealloc может ожидать, что все будет очищено немедленно, и запуск его в другом потоке означает, что он будет асинхронным, если только вы не использовали waitUntilDone:YES. Попробуйте и дайте нам знать! 11.09.2011

  • 2

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

    dispatch_async(dispatch_get_main_queue(), ^{
    
        // do work here
    
    });
    
    13.04.2012

    3

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

    - (void)viewWillDisappear:(BOOL)animated {
      [operationQueue cancelAllOperations];
      [super viewWillDisappear:animated;
    }
    
    03.06.2009
  • Спасибо за ответ. Но я добавил это, и кажется, что Dealloc всегда вызывается во вторичном потоке. До сих пор не могу понять причину. 03.06.2009
  • Отмена фоновых операций — правильный способ сделать это. Причина, по которой это не решает проблему, заключается в том, что cancelAllOperations не прерывает автоматически уже запущенные операции. Что вам нужно сделать, так это в вашем методе load после сна потока проверить свойство isCancelled операции; если да, вернитесь без вызова done. Но если все, что вы делаете, это просто приостанавливаете фоновый поток, проще всего это сделать с помощью performSelector:withObject:afterDelay:. 25.04.2011

  • 4

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

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

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

    31.07.2009
  • Ваше «обходное» решение работает для меня, спасибо. Кстати, Тао, каково ваше последнее решение о том, какой метод вы используете? 10.09.2009
  • на самом деле у вас утечка памяти...!! 03.03.2011

  • 5

    Я пытался :

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

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

    27.09.2010

    6

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

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

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

    03.06.2009

    7

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

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

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

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

    11.09.2009

    8
  • когда вы публикуете код, желательно добавить краткое объяснение 23.09.2012
  • Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

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

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..