web-dev-qa-db-ja.com

viewWillDisappearでロード中のUIWebViewを安全にシャットダウンする方法は?

私はグーグルマップをロードしているUIWebViewを含むビューを持っています(とてもたくさんのjavascriptなど)。私が抱えている問題は、Webビューの読み込みが完了する前にユーザーがナビゲーションバーの「戻る」ボタンを押すと、Webビューに読み込みを停止してから解放するように、取得せずにきちんと指示する方法が明確でないことです。割り当て解除されたインスタンスに送信されるメッセージ。また、Webビューが、コンテナビューが完了する前に消えることを好むかどうかもわかりません(ただし、ユーザーがロードする前に戻るボタンを押すかどうかは選択できません)。

私のviewWillDisappearハンドラーで私はこれを持っています

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

デリゲートをnilすると、didFailLoadWithErrorがビューコントローラに送信されなくなるため、これでほとんどの場合は問題なく処理できるようです。ただし、ビューのdeallocメソッドでWebビューを解放すると、(断続的に)メッセージが割り当て解除されたインスタンスに送信されることがあります。これは、実際のページで実行されているjavascriptに関連しているようです。例:

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

単にWebビューを解放しないと、Webビューがリークしていると思いますが、これらのメッセージは表示されません。

'stopLoading'メッセージを送信せず、viewWillDisappear内でWebビューを解放するだけの場合、次のようなメッセージが表示されます。

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

おそらく関連して、私は時々(再び完全に断続的に)醜いheisenbugを受け取り、他のビューのナビゲーションバーの戻るボタンをクリックするとタイトルがポップされますが、ビューはポップされません。言い換えると、スタックにビューnのタイトルが残っていますが、表示されているビューは引き続きビューn + 1です(その結果、この画面に閉じ込められ、ルートビューに戻ることができません-移動できます他の方向、つまり、より多くのビューをプッシュして、正しくポップされなかったビューにポップバックします。ルートビューだけではありません。唯一の方法は、アプリを終了することです)。また、同じビューで同じ一連のプッシュとポップが正常に機能する場合もあります。

この特定のものは私を狂わせています。これは、Webビューが読み込まれる前にビューが消えることに関連している可能性があると思います。つまり、この場合、メモリに落書きしてビュースタックを混乱させる可能性があると思います。または、これは完全に無関係で、どこか別のバグである可能性があります(デバッグビルドモードで再現できなかったため、リリースビルド設定で発生するのはgdbで表示できない場合のみです:-)。デバッグの実行から、私は何も過剰にリリースしているとは思いません。そして、ある時点でWebビューのあるビューにヒットした場合にのみトリガーできるようで、その直後には発生しません。

31
frankodwyer

これを変更すると、リークとゾンビの両方の問題が修正されるはずです。

- (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];
}
53
rpetrich

それを処理する方法はいくつかありますが、これは機能するはずです。 didFailLoadWithErrorメッセージが必要な場合は、停止したことを通知します。

フラグを設定しますisLeaving = YES; WebviewにstopLoadingを送信します。

DidFailLoadWithError:で、Webビューが停止したときに発生するエラーを確認します。

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

[otherClass performSelector:@selector(shootWebview)withObject:nil withDelay:0]

}

shootWebviewでwebViewを解放します。


バリエーション:それについてもっと騎士になりたい場合は、performSelector:withObject:withDelay:を[fillintheblank]の遅延で実行し、チェックなしで10〜30秒呼び出すと、ほぼ確実に回避できます。私はそれをお勧めしません。

DidFailLoadWithErrorにフラグを設定して、別の場所でクリーンアップすることができます。

または私のお気に入り、多分あなたが去るときにそれをすべて取り消す必要はありません。そのビューコンテナをもう一度表示しませんか?再利用してみませんか?

デバッグがリリースの問題とは異なるため、構成をチェックして、まったく同じであることを確認することをお勧めします。賞金は質問の再現可能な部分にありましたよね? ;-)。

-ちょっと待ってください。WebViewでViewコンテナ全体を停止している可能性があります。上記のバリエーションを実行して、shootWebViewでコンテナ全体が解放されるのを待つことができます。

1
dieselmcfadden

投稿の後半で説明しているUINavigationControllerのバグは、メモリ警告の処理に関連している可能性があります。私はこの現象を経験し、スタック内のビュー(n + 1)を表示しながらメモリ警告をシミュレートすることにより、スタック内のビューnでそれを再現することができました。

UIWebViewはメモリを消費するため、ビュー階層の一部として使用されている場合、メモリ警告が表示されることは驚くことではありません。

1
Armaggi

OS3でUIWebViewを使用すると、これと同様の問題が発生しました。この説明は良い出発点でしたが、webViewをリリースする前にWebビューデリゲートを単に削除するよりも問題が解決したことがわかりました。

サンプルコード(受け入れられた答え-上記)を読む-それは多くのやり過ぎのようです。例えば。 [webViewリリース]とwebView = nil行は、作成者が変数を宣言する方法を考えると、まったく同じことを行います(したがって、両方は必要ありません)。また、すべての保持ラインと解放ラインについても完全には確信していませんが、マイレージは異なると思います。

0
TimM

おそらく関連して、私は時々(再び完全に断続的に)醜いheisenbugを受け取り、他のビューのナビゲーションバーの戻るボタンをクリックするとタイトルがポップされますが、ビューはポップされません。言い換えると、スタックにビューnのタイトルが残っていますが、表示されているビューは引き続きビューn + 1です(その結果、この画面に閉じ込められ、ルートビューに戻ることができません-移動できます他の方向、つまり、より多くのビューをプッシュして、正しくポップされなかったビューにポップバックします。ルートビューだけではありません。唯一の方法は、アプリを終了することです)。また、同じビューで同じ一連のプッシュとポップが正常に機能する場合もあります。

同じ問題があります。スタックが2を超え、現在のビューコントローラーインデックスが2を超えるナビゲーションコントローラーを使用している場合、この瞬間にmemoryWarningが発生すると、同じ問題が発生します。

本質的に1つの解決策があります。これは、NavigationControllerのpopメソッドとPushメソッドのオーバーライド、ビューコントローラーのスタック、スタックされた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つのViewControllerで正常に機能します。

0
abuharsky

release内の単純なdeallocメッセージで十分なはずです。

2番目の問題は、ビューの割り当てが時期尚早に割り当てられていないように聞こえますが、コードを見ないと多くを語ることはできません。

0
Can Berk Güder