Как безопасно завершить загрузку UIWebView в viewWillDisappear? - PullRequest
31 голосов
/ 26 апреля 2009

У меня есть представление, содержащее UIWebView, который загружает карту Google (так много javascript и т. Д.). У меня проблема в том, что если пользователь нажимает кнопку «назад» на панели навигации до того, как веб-представление завершит загрузку, мне не понятно, как аккуратно сказать веб-представлению прекратить загрузку, а затем отпустить его, не получая сообщения, отправленные на освобожденный экземпляр. Я также не уверен, что веб-представлению нравится, когда его контейнерное представление исчезает до его завершения (но у меня нет выбора, если пользователь нажимает кнопку «Назад» перед загрузкой).

На мой взгляд обработчик исчезновения / исчезновения У меня есть этот

map.delegate=nil;
[self.map stopLoading];

похоже, что в большинстве случаев это нормально, так как обнуление делегата останавливает его, посылая didFailLoadWithError моему контроллеру представления. Однако, если я освобождаю веб-представление в методе dealloc моего представления, иногда (с перерывами) я все равно получаю сообщение, отправляемое на экземпляр освобождения, который, по-видимому, связан с javascript, запущенным на фактической странице, например ::

-[UIWebView webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:]: message sent to deallocated instance 0x4469ee0

Если я просто не выпускаю веб-просмотр, то я не получаю эти сообщения, хотя, полагаю, я пропускаю веб-просмотр.

Если я не отправляю сообщение «stopLoading» и просто выпускаю веб-представление в viewWillDisappear, то я вижу сообщения, подобные этому:

/SourceCache/WebCore/WebCore-351.9.42/wak/WKWindow.c:250 WKWindowIsSuspendedWindow:  NULL window.

Возможно, это связано с тем, что я иногда (опять-таки, с перебоями) получаю некрасивый гейзенбаг, где при нажатии кнопки «Назад» на навигационной панели другого вида появляется заголовок, но не вид. Другими словами, я остался с заголовком представления n в стеке, но показ по-прежнему остается представлением n + 1 (в результате вы попали на этот экран и не можете вернуться к корневому представлению - вы можете перейти к другое направление, то есть подтолкнуть больше представлений и вернуться к представлению, которое не появилось правильно, просто не к корневому представлению. Единственный выход - это выйти из приложения). В других случаях та же последовательность нажатий и всплывающих окон в тех же видах работает нормально.

Именно этот человек сводит меня с ума. Я думаю, что это может быть связано с исчезновением представления перед загрузкой веб-представления, т. Е. В этом случае я подозреваю, что оно может набросать память и спутать стек представлений. Или это может быть совершенно не связано, и это может быть ошибка где-то еще (я никогда не смог воспроизвести его в режиме отладочной сборки, это происходит только с настройками сборки релиза, когда я не могу смотреть его с помощью gdb :-). Из моих отладочных прогонов я не думаю, что переиздаю что-либо. И я, кажется, могу запустить его, только если в какой-то момент я нажал на представление, имеющее веб-представление, и это не произойдет сразу после этого.

Ответы [ 6 ]

52 голосов
/ 06 мая 2009

Вариант этого должен исправить проблемы с утечками и зомби:

- (void)loadRequest:(NSURLRequest *)request
{
    [self retain];
    if ([webView isLoading])
        [webView stopLoading];
    [webView loadRequest:request];
    [self release];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
    [self retain];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [self release];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [self release];
}

- (void)viewWillDisappear
{
    if ([webView isLoading])
        [webView stopLoading];
}

- (void)dealloc
{
    [webView setDelegate:nil];
    [webView release];
    [super dealloc];
}
1 голос
/ 30 июня 2009

Ошибка UINavigationController, которую вы описываете во второй части вашего поста, может быть связана с обработкой предупреждений памяти. Я испытал это явление, и мне удалось воспроизвести его при просмотре n в стеке, имитируя предупреждение памяти при просмотре представления (n + 1) в стеке.

UIWebView - это пожиратель памяти, поэтому получение предупреждений о памяти не удивительно, если его использовать как часть иерархии представлений.

1 голос
/ 03 мая 2009

Есть несколько способов справиться с этим, но это должно сработать. Вам нужно сообщение didFailLoadWithError, это то, что говорит вам, что оно остановлено.

Установить флаг isLeaving = YES; Отправьте веб-просмотр stopLoading.

В didFailLoadWithError: проверьте наличие ошибки, которую вы получаете, когда веб-просмотр останавливается:

if ((thiserror.code == NSURLErrorCancelled) && (isLeaving == YES)) {

[otherClass executeSelector: @selector (shootWebview) с объектом: ноль с задержкой: 0]

}

выпуск веб-просмотра в режиме съемки. Веб-обзор:


вариация: если вы хотите быть кавалернее об этом, вы можете сделать executeSelector: withObject: withDelay: с задержкой [fillintheblank], назвать его 10-30 секунд без проверки, и вы почти наверняка сойдете с рук, хотя я не рекомендую.

Вы можете сделать didFailLoadWithError установить флаг и очистить его где-нибудь еще.

или мой любимый, может быть, вам не нужно разбираться со всем этим, когда вы уходите. Вы когда-нибудь снова отобразите этот контейнер? почему бы не держать его снова использовать?

Ваша отладка отличается от выпуска, вы можете проверить свою конфигурацию, чтобы убедиться, что она точно такая же. Баунти был на воспроизводимой части вопроса, верно? ; -.)

- О, подождите секунду, возможно, вы взяли весь контейнер View с помощью WebView. Вы можете внести изменения в вышеперечисленное и подождать, чтобы освободить весь контейнер в shootWebView.

0 голосов
/ 08 сентября 2009

Возможно, связано, я иногда (снова прерывистый) уродливый Гейзенбаг, где нажав на спину кнопка на панели навигации другого вида появится название, но не вид. Другими словами, я остаюсь с название представления в стеке, но вид, показывающий все еще вид n + 1 ( В результате вы попали в ловушку на этом экран и не может вернуться в корень вид - ты можешь идти в другом направлении, то есть подтолкнуть больше просмотров и вернуться к мнение, которое не появилось правильно, просто не в корне зрения. Единственный выход - выйти из приложения). На другом раз та же последовательность толчков и всплывает на тех же представлениях работает отлично.

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

Существует одно решение, которое я нашел после многих экспериментов с переопределением методов pop и push в NavigationController, со стеком контроллеров представлений, с представлениями и суперпредставлениями для стекированных контроллеров ViewController и т. Д.

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface FixedNavigationController : 
UINavigationController <UINavigationControllerDelegate>{

}

@end

#import "FixedNavigationController.h"

static BOOL bugDetected = NO;

@implementation FixedNavigationController

- (void)viewDidLoad{
    [self setDelegate:self];
}

- (void)didReceiveMemoryWarning{
    // FIX navigationController & memory warning bug
    if([self.viewControllers count] > 2)
        bugDetected = YES;
}

- (void)navigationController:(UINavigationController *)navigationController 
didShowViewController:(UIViewController *)viewController 
animated:(BOOL)animated
{

    // FIX navigationController & memory warning bug
    if(bugDetected){
        bugDetected = NO;

        if(viewController == [self.viewControllers objectAtIndex:1]){
            [self popToRootViewControllerAnimated:NO];
            self.viewControllers = [self.viewControllers arrayByAddingObject:viewController];
        }
    }
}

@end

Отлично работает для 3 контроллеров представления в стеке.

0 голосов
/ 17 августа 2009

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

Чтение примера кода (принятый ответ - выше) - кажется, это слишком много. Например. [webView release] и webView = nil строки делают одно и то же, учитывая то, как автор описывает переменную (поэтому вам не нужны оба). Я также не полностью убежден всеми линиями удержания и выпуска - но я предполагаю, что ваш пробег будет отличаться.

0 голосов
/ 26 апреля 2009

Простого release сообщения в dealloc должно быть достаточно.

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

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