UIWebView - CFData 何时(或如何)发布?
经过几天的头撞墙和不眠之夜之后,我希望在这里找到一些帮助。我在这里浏览了各种帖子,但似乎没有一个答案能为我提供解决方案。
简而言之,我的问题是我的应用程序在大量使用(> 10分钟)UIWebView(例如依次打开更大的新闻报纸网站 - 一个接一个)后崩溃。
提供更多详细信息: 我正在编写一个 iPhone 应用程序,并从 MainViewController 中将 browserViewController 推送到 navigationController 上。 browserViewController 加载一个包含 UWebView 的笔尖(我不以编程方式创建 WebView)。 UIWebView 使用 Interface Builder 连接。
当返回 Main 然后再次返回 browserViewController 时,我仅在 browserViewController 为零时重新创建它。 (我想保留 UIWebView 中加载的内容 - 仅当出现内存警告时才应卸载该视图并释放所有使用的内存)。
在 MainViewController 和 browserViewController 中,我都在响应内存警告,但这似乎并不能提供足够的缓解。
查看 Instruments,我注意到例如 CFData(store) 不断增加。即使我模拟内存警告(请参阅下面的代码)并在 browserViewController 上调用 viewDidUnload,CFData 仍保持分配状态并且不会被释放。
所以我最大的问题是:
如何释放“浏览”创建的内存?
这分为两种情况:
- 如何确保 viewDidUnload 正确释放分配给我的 CFData(store) 的内存?
- 当用户不断在 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 = @""; [browserWebView loadHTMLString:html baseURL:nil];
2) 在上述代码的不同位置使用removeAllCachedResponses
[[NSURLCache共享URLCache]removeAllCachedResponses];
3) setSharedURLCache 也没有提供缓解(我也在AppDelegate applicationDidFinishLaunching 中使用了它)。
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache]; [sharedCache释放];
不幸的是,这些都没有帮助“清除缓存”或释放 CFData(store) 分配的内存。
如果有人能对此有所启发并让我知道我错过了什么或做错了什么,我将不胜感激。
.
。
编辑:
在 KiwiBastard 最初回复后,我添加了一个屏幕截图,显示了我在 Instruments 中观察到的内容:
。
。
2010 年 6 月起编辑:
我还是没能解决这个问题。
在第二次尝试中,我完全以编程方式创建了 UIWebView。
还是同样的问题。然而我注意到一个奇怪的行为。例如,如果我将 PDF 文档加载到 webView 中并且不向上或向下滚动 PDF 页面,则 webView 和 PDF 页面将被加载到 WebView 中。数据发布成功。然而,一旦我滚动到第二页,dealloc 将不再起作用,并且我的应用程序最终会在某个时刻耗尽内存。这很奇怪,我无法解决这个问题。
有人有什么想法吗?帮助?
after multiple days of banging my head against the wall and having sleepless nights I'm hoping to find some help here. I've gone through various posts here, but none of the answers seem to provide a resolution for me.
In short, my problem is that my App crashes after heavy usage (>10min) of the UIWebView (e.g. opening larger news paper sites in series - one after the other).
To give more details:
I am writing an iPhone App and from the MainViewController I push a browserViewController on the navigationController. The browserViewController loads a nib which contains a UWebView (I do not create the WebView programatically). The UIWebView is wired up using Interface Builder.
When going back to Main and then going again to the browserViewController, I only recreate the browserViewController if it is nil. (I want to keep the content that is loaded i the UIWebView - only if there is a memory warning shoud this view be unloaded and release all memory used).
In both, MainViewController and browserViewController I am responding to memory warnings, but this seems not to provide enough relief.
Looking at Instruments I noticed that for example CFData(store) keeps increasing. And even if I simulate a memory warning (see code below) and call viewDidUnload on browserViewController, CFData remains allocated and does not get freed.
So my biggest question is:
How to free up memory created from "browsing"?
This counts for two cases:
- how do I make sure that viewDidUnload properly frees memory allocated my CFData(store)?
- how to free up memory when the user keeps loading pages in browserViewController?
.
Who manages CFData?
See below for my simplified sample code:
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
I have tried various recommendations from other posts. For example:
1) Loading an empty page into the WebView.
NSString *html = @"<html><head></head><body></body></html>"; [browserWebView loadHTMLString:html baseURL:nil];
2) using removeAllCachedResponses on various places in the above code
[[NSURLCache sharedURLCache] removeAllCachedResponses];
3) setSharedURLCache did also not provide relief ( I also used this in the AppDelegate applicationDidFinishLaunching).
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:sharedCache]; [sharedCache release];
Unfortunately none of this has helped to "clear the cache" or to free memory allocated by CFData(store).
If anyone could shine some light on this and let me know what I'm missing or doing wrong I would greatly appreciate this.
.
.
Edit:
After the initial reply from KiwiBastard I added a screen shot that shows what I observe in Instruments:
.
.
Edit from June 2010:
I have still not been able to solve this.
In a second attempt, I created the UIWebView completely programmatically.
Still same issue. However I noticed a strange behavior. If I load for example a PDF document into the webView and I do not scroll the PDF page up or down, the webView & data gets successfully released. However as soon as I scroll to the second page, the dealloc won't work any longer and my App ends up running out of memory at some point. This is totally strange and I cannot get this resolved.
Anyone any idea? Help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
要释放CFData,您只需调用CFRelease(您的CFData对象名称)。
To release CFData you only need to call CFRelease(your CFData object name).
我认为可能发生的情况是您的浏览器永远不会被释放,并且 viewDidUnload 可能永远不会被调用。
因为您的 MainViewController 有一个从未释放的 BrowserViewController 类型的变量,所以它将在您的应用程序的生命周期中驻留。另外,因为您只是切换视图,所以视图也将保留在内存中。
我可以建议您尝试在需要时创建 BrowserViewController 变量,并在导航控制器推送后释放它,例如
我知道这会稍微影响性能,因为它每次都必须加载笔尖,但您显然不希望无论如何都要缓存 vc 吗?
I think what could be happening is that your Browser is never deallocated, and the viewDidUnload is probably never being called.
Because your MainViewController has a variable of type BrowserViewController that is never released, that will be resident for the life of your app. Also because you are only switching views, the view will stay in memory too.
Can I suggest you try creating the BrowserViewController variable when you need it, and release it once it has been pushed by the navcontroller eg
I know that it will slightly effect performance because it has to load the nib everytime, but you distinctly don't want to cache the vc anyway?
我在某处读到,这是 UIWebView 的一个众所周知的错误。有人说使用静态 webview 对象来避免一次又一次地初始化它,但找不到合适的解决方案。即使你也遵循同样的方法。幸运的是,我的要求是带有图像的普通网络视图。因此,我最终使用了带有 UIImageView 和 UITextView 的自定义控制器,无需进行编辑。对我来说效果很好。
Somewhere I read, this is a well known bug with UIWebView. Some says to use a static webview object to avoid initializing it again and again but couldn't find a proper solution. Even you follows the same approach. Luckily my requirement was a plain web view with an image. So I ended up using a custom controller with a UIImageView and a UITextView without editing. Works fine for me.