UIWebView - Когда (или как) выпускается CFData? - PullRequest
14 голосов
/ 21 октября 2010

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

Короче говоря, моя проблема заключается в том, что мое приложение аварийно завершает работу после интенсивного использования (> 10 минут) UIWebView (например, при открытии крупных сайтов газетных статей последовательно - один за другим).

Для более подробной информации: Я пишу приложение для iPhone, и из MainViewController я помещаю browserViewController в навигационный контроллер. BrowserViewController загружает перо, которое содержит UWebView (я не создаю WebView программно). UIWebView подключается с помощью Interface Builder.

Когда я возвращаюсь в Main, а затем снова возвращаюсь к browserViewController, я воссоздаю browserViewController, только если он равен нулю. (Я хочу сохранить загруженный контент в UIWebView - только при наличии предупреждения о памяти, если это представление будет выгружено и освободит всю использованную память).

В обоих случаях, MainViewController и browserViewController Я отвечаю на предупреждения памяти, но, похоже, это не дает достаточного облегчения.

Глядя на инструменты, я заметил, что, например, CFData (магазин) продолжает расти. И даже если я имитирую предупреждение памяти (см. Код ниже) и вызываю viewDidUnload для browserViewController, CFData остается выделенным и не освобождается.

Итак, мой самый большой вопрос:
Как освободить память, созданную в результате "просмотра"?

Это относится к двум случаям:
- как мне убедиться, что viewDidUnload правильно освобождает память, выделенную моим CFData (хранилище)?
- как освободить память, когда пользователь продолжает загружать страницы в 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 также не предоставил облегчение (я также использовал это в приложении AppDelegateDidFinishLaunching).

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

К сожалению, ничего из этого не помогло «очистить кеш» или освободить память, выделенную CFData (хранилище).

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

.
,

Edit:
После первоначального ответа от KiwiBastard я добавил снимок экрана, на котором показано, что я наблюдаю в инструментах:

enter image description here

.
,

Редактировать с июня 2010 г .:
Я до сих пор не смог решить эту проблему.
Во второй попытке я полностью программно создал UIWebView.
Все та же проблема. Однако я заметил странное поведение. Если я загружаю, например, документ PDF в webView и не прокручиваю страницу PDF вверх или вниз, webView & data успешно освобождаются. Однако, как только я прокручиваю до второй страницы, dealloc больше не будет работать, и в какой-то момент моему приложению не хватит памяти. Это совершенно странно, и я не могу решить эту проблему.
Кто-нибудь есть идеи? Помогите?

Ответы [ 3 ]

2 голосов
/ 25 июля 2011

Для освобождения CFData вам нужно только вызвать CFRelease ( имя вашего объекта CFData ) .

1 голос
/ 18 февраля 2012

Где-то я читал, это хорошо известная ошибка в UIWebView.Некоторые говорят, что нужно использовать статический объект webview, чтобы избежать его инициализации снова и снова, но не смогли найти правильного решения.Даже ты придерживаешься того же подхода.К счастью, моим требованием было простое веб-представление с изображением.В итоге я использовал собственный контроллер с UIImageView и UITextView без редактирования.У меня отлично работает.

1 голос
/ 21 октября 2010

Я думаю, что может случиться так, что ваш браузер никогда не освобождается, и 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?

...