14

頭を壁にぶつけて眠れぬ夜を過ごした数日後、ここで助けを見つけたいと思っています。ここでさまざまな投稿を行いましたが、どの回答も解決策を提供していないようです。

要するに、私の問題は、UIWebView を頻繁に (10 分以上) 使用した後にアプリがクラッシュすることです (たとえば、大きなニュース ペーパー サイトを連続して開くなど)。

詳細を説明するには: iPhone アプリを作成していて、MainViewController から、navigationController に browserViewController をプッシュします。browserViewController は、UWebView を含む nib をロードします (プログラムで WebView を作成しません)。UIWebView は、Interface Builder を使用して接続されています。

Main に戻ってから browserViewController に戻ると、nil の場合のみ browserViewController を再作成します。(UIWebViewにロードされたコンテンツを保持したい-メモリ警告がある場合のみ、このビューをアンロードし、使用されているすべてのメモリを解放する必要があります)。

MainViewController と browserViewController の両方で、メモリ警告に応答していますが、これでは十分な緩和が得られないようです。

Instruments を見ると、たとえば CFData(store) が増加し続けていることに気付きました。また、メモリ警告をシミュレートして (以下のコードを参照)、browserViewController で viewDidUnload を呼び出しても、CFData は割り当てられたままになり、解放されません。

だから私の最大の質問は
、「ブラウジング」から作成されたメモリを解放する方法は?

これは 2 つのケース
に当てはまります。
- ユーザーが browserViewController でページをロードし続けるときにメモリを解放する方法は?
.
誰が CFData を管理しますか?

私の単純化されたサンプルコードについては、以下を参照してください。

MainViewController.h

//  MainViewController.h
#import "myAppDelegate.h"
#import "BrowserViewController.h"

@interface MainViewController : UIViewController {
    BrowserViewController *browViewController;
}

- (void) switchToBrowserViewController;

@end

MainViewController.m

//  MainViewController.m
#import "MainViewController.h"

@implementation MainViewController

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    [browViewController release];
    browViewController = nil;
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)dealloc {
    [browViewController release];
    browViewController = nil;
    [super dealloc];
}

- (void) switchToBrowserViewController {

    // create new browViewController if needed
    if ( browViewController == nil ) {
        browViewController = [[BrowserViewController alloc]     initWithNibName:@"BrowserViewController" bundle:nil];
    }

    browViewController.navigationItem.hidesBackButton = YES;

    [((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration: 1];
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:
  ((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];

    [((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];
    [UIView commitAnimations];

}

@end

BrowserViewController.h

//  BrowserViewController.h

#import <UIKit/UIKit.h>
#import "myAppDelegate.h"

@interface BrowserViewController : UIViewController <UIWebViewDelegate> {
    IBOutlet UITextField *browserURLField;
    IBOutlet UIWebView *browserWebView;
}

@property (nonatomic, retain) UIWebView *browserWebView;

- (void) loadURLinBrowser;

@end

BrowserViewController.m

//  BrowserViewController.m
#import "BrowserViewController.h"

@implementation BrowserViewController
@synthesize browserWebView;

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

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload { 
    [super viewDidUnload];

    [browserWebView stopLoading];
    [browserWebView setDelegate:nil];
    [browserWebView removeFromSuperview];
    [browserWebView release];
    browserWebView = nil;

    browserURLField = nil;
}

- (void)dealloc {
    [browserURLField release];

    browserWebView.delegate = nil;
    [browserWebView stopLoading];
    browserWebView = nil;
    [browserWebView release];

    [super dealloc];
}

- (void) switchBackToMainViewController {

    [((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:NO animated:NO];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration: 1];
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];

    [((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController popViewControllerAnimated:NO];

    [UIView commitAnimations];
}

- (void) loadURLinBrowser {
    NSURL *url = [[NSURL alloc] initWithString:browserURLField.text];
    NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
    [browserWebView loadRequest: request];
    [request release];
    [url release];
}

@end

他の投稿からさまざまな推奨事項を試しました。例えば:

  • 1) 空のページを WebView に読み込みます。

        NSString *html = @"<html><head></head><body></body></html>";
        [browserWebView loadHTMLString:html baseURL:nil];
    
  • 2) 上記のコードのさまざまな場所で removeAllCachedResponses を使用する

        [[NSURLCache sharedURLCache] removeAllCachedResponses];
    
  • 3) setSharedURLCache も救済策を提供しませんでした (AppDelegate アプリケーションの DidFinishLaunching でもこれを使用しました)。

        NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
        [NSURLCache setSharedURLCache:sharedCache];
        [sharedCache release];
    

残念ながら、これはどれも「キャッシュをクリア」したり、CFData(store) によって割り当てられたメモリを解放したりするのに役立ちませんでした。

誰かがこれに光を当てて、私が見逃していることや間違っていることを教えてくれれば、私はこれを大いに感謝します.

.
.

編集:
KiwiBastard からの最初の返信の後、Instruments で観察したことを示すスクリーン ショットを追加しました。

ここに画像の説明を入力

.
.

2010年6月から編集:
私はまだこれを解決できていません。
2 回目の試行では、UIWebView を完全にプログラムで作成しました。
まだ同じ問題。しかし、私は奇妙な行動に気づきました。たとえば、PDF ドキュメントを webView にロードし、PDF ページを上下にスクロールしないと、webView とデータが正常に解放されます。ただし、2 番目のページにスクロールするとすぐに、dealloc が機能しなくなり、ある時点でアプリのメモリが不足してしまいます。これはまったく奇妙で、解決できません。
誰でもアイデアはありますか?ヘルプ?

4

3 に答える 3

2

CFDataを解放するには、 CFRelease( your CFData object name )を呼び出すだけです。

于 2011-07-25T12:11:26.167 に答える
1

どこかで読んだところによると、これは UIWebView のよく知られたバグです。静的 webview オブジェクトを使用して何度も初期化するのを避けるように言う人もいますが、適切な解決策を見つけることができませんでした。あなたも同じアプローチに従います。幸いなことに、私の要件は、画像付きのプレーンな Web ビューでした。そのため、編集せずに UIImageView と UITextView を持つカスタムコントローラーを使用することになりました。私にとってはうまくいきます。

于 2012-02-18T05:24:29.793 に答える
1

ブラウザの割り当てが解除されず、おそらく viewDidUnload が呼び出されないことが考えられます。

MainViewController には、解放されることのない BrowserViewController 型の変数があるため、アプリの存続期間中常駐します。また、ビューを切り替えるだけなので、ビューもメモリに残ります。

必要に応じて BrowserViewController 変数を作成し、navcontroller によってプッシュされたら解放することをお勧めします。

 BrowserViewController *browViewController = [[BrowserViewController alloc]     initWithNibName:@"BrowserViewController" bundle:nil];

 browViewController.navigationItem.hidesBackButton = YES;

 [((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];

 [UIView beginAnimations:nil context:NULL];
 [UIView setAnimationDuration: 1];
 [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:
  ((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];

 [((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];
 [UIView commitAnimations];
 [browViewController release];

毎回ペン先をロードする必要があるため、パフォーマンスにわずかに影響することはわかっていますが、とにかくVCをキャッシュしたくないのは明らかですか?

于 2010-10-20T23:16:48.823 に答える