Rapidly increasing memory due to load url in UIWebview ios - ios

I am loading URL in UIWebview and it is working fine.But during loading of web request it is consuming memory too much.Whenever i load some URL in UIWebview memory is increased from 30mb to 95mb and based on each link clicked in UIWebview it is still increasing and reach to 180mb and so on.I used some code to remove memoryin UIWebview.But there is no benefit.I have done memory management by Analyze in Xcode but there are no leaks and i also check allocations and leaks.It is just creating one instance of UIWebview.
This is my code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
int cacheSizeMemory = 4*1024*1024; // 4MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"] autorelease];
[NSURLCache setSharedURLCache:sharedCache];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
ViewController.h
#property(nonatomic,strong)UIWebview *webview;
ViewController.m
#synthesize webview;
-(void)viewdidLoad
{
NSString *urlString = #"some url";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webview=[uiwebview alloc]init];
[self.webview loadRequest:request];
}
/*webview delegates
-(void)viewWillDisappear:(Bool)animated
{
self.webview=nil;
[self.webview removefromSuperView]
[self.webview loadHtmlString:#"" baseUrl:nil]
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:NSURLRequest];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
{
if([[cookie domain] isEqualToString:someNSStringUrlDomain]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
I am facing this issue for few days.Please give me some alternative or method to remove UIWebviewmemory.

In viewWillDisapear You are setting your WebView reference to nil so the removing in the next line fails.
Try setting your reference to nil after you completed all cleanup around the WebView, i.e. in the last line of the method.
You probably also need to remove the delegate of the WebView (before nilling).

As dogsgod is pointed out the way OP release memory is not proper, but
there are other things which should mention for this type of question.
If you are developing new application I suggest you to use ARC for your project
If its not possible to implement ARC then follow below advice
In viewdidLoad() you are allocating object that's good but its better if you first check that is object is already allocated of not (as it may change that it is allocated before pushing it into navigation).
Sample Code
if(self.webview!=nil)
{
self.webview = nil; // release memory
}
// Code for allocating memory.
If you alloc object in viewDidLoad() then must be deallco in dealloc() method.
NOTE:
When you assign any object nil, it means now object is not point to the old location and you have no control over that object, so calling some method like [self.webview removefromSuperView] after assigning nil to webview it have no effect.
Extra note:
Please read this document for knowledge about methods like viewDidload,viewWillAppear ... etc.

Related

NSURLConnection is growing in instruments, is this NSURLCache?

So I have a memory problem with my app. The app has a MKMapView with MKOverlayRenderer which loads images on the map. Everything working ok but after 30-60 minutes the app crashes due memory.
With Instruments I found that NSURLConnection is growing and because I cannot find anything else (besides all kind of stuff I dont understand) I think thats is my problem.
Screenshot instruments after runnning for 1-2 minutes:
Screenshot instruments after running for 12-13 minutes:
Images are loaded like this in the canDrawMapRect method and saved to tmp folder (if I turn this off the NSURLConnection won't grow that high):
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
[NSURLConnection sendAsynchronousRequest:request
queue:[OperationQueues sharedOperationQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[data writeToFile:path atomically:YES];
Then loaded in the drawMapRect method.
I tried fixing this with the following code:
AppDelegate:
int cacheSizeMemory = 4*1024*1024; // 4MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory
diskCapacity:cacheSizeDisk
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
ViewController:
- (void)didReceiveMemoryWarning
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[super didReceiveMemoryWarning];
}
right after the request: [added]
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
But as you can see in Instruments without any luck.
Could this be my problem?
Is this the NSURLCache which is growing or something else?
As requested a couple more screenshots from instruments:
First, 400 network requests in one minute seems pretty excessive. Some of those requests are probably duplicated or unnecessary. The amount of calls is obviously downloading a lot of information, and not giving the os much time to cleanup.
Second, your NSURLCache is actually providing more memory space than the default cache. I tested on my device/simulator and ios provides a default memory capacity of 512 000 bytes. You're caches are initialized with 4 194 304 bytes (and way more on disk).
I would suggest that you set your cache size smaller to see some improvement. However, investigating the amount of network requests that are made will provide longer term benefits.

NSURLCache does not work offline with UIWebVIew

I am trying to load UIWebView Offline with data from NSURLCache.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// setup cache
int cacheSizeMemory = 4*1024*1024; // 4MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:urlCache];
}
-(void)loadWebView{
request = [NSURLRequest requestWithURL:myURL cachePolicy: NSURLRequestReturnCacheDataElseLoad timeoutInterval:1000];
[self.webView loadRequest:request];
}
When I try to load UIWebView offline it calls didFailWithError instead of connectionDidFinishLoading. My Cache directory however contains offline data and resources like css, js files but UIWebView is not able to access it.
I have confirmed this by overriding the following method:
-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSCachedURLResponse *response = [super cachedResponseForRequest:request];
if([response.data length]> 0){
// this is called for all css , js script files
NSLog(#"Returning data from cache");
}
return response;
}
Moreover, the data is being returned from cache UIWebview fails to load it.

iOS Memory issue (ARC) when downloading and saving large ammount of images from server

The following code downloads 700+ images from a server with varying sizes, the issue here is that memory (even with ARC) is never released and eventually a memory warning appears followed by the application exiting. I've tried #autoreleasepool in this method and that didn't seem to work. Also I've tried stopping the for loop at different locations to see if memory is released after it finished, but it isn't.
This method is called inside a for loop and receives the image url and short name. It has been tried in a background thread and main thread with the same results (memory wise).
-(void)saveImage:(NSString*)image name:(NSString*)imageName{
int k = 0;
for (int j = 0; j < [imageName length]; j++) {
if ([imageName characterAtIndex:j] == '/') {
k = j;
}
}if (k != 0) {
imageName = [imageName substringFromIndex:k+1];
}
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", imageName]];
if ([fileManager fileExistsAtPath:fullPath]) {
[fileManager removeItemAtPath:fullPath error:nil];
}
NSURL *url = [NSURL URLWithString:image];
NSData *data = [[NSData alloc]initWithContentsOfURL:url];
NSLog(#"Saved: %d:%#", [fileManager createFileAtPath:fullPath contents:data attributes:nil], url);
data = nil;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
int cacheSizeMemory = 4*1024*1024; // 4MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
NSLog(#"mem warning, clearing cache");
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
Based on your screenshot, I think the issue is with the NSURL caching rather than the actual NSData objects. Can you try the following:
In your Application Delegate, setup an initial URL cache:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Do initial setup
int cacheSizeMemory = 16*1024*1024; // 16MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
// Finish the rest of your didFinishLaunchingWithOptions and head into the app proper
}
Add the following to your Application Delegate:
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
A cache file will be created:"Library/Caches/your_app_id/nsurlcache"
The link to the Apple example is here: URL Cache
Code not tested but this (or something similar) should sort your problem + plus you can experiment with the cache sizes.
Can you post another screenshot of Allocations in action with this code? I would expect to see memory use stop growing and flatten out.
"dataWithContentsOfURL" returns an autoreleased NSData object and that doesn't normally get released until the end of the run loop or the end of the method, so it's no wonder you're filling up memory pretty quickly.
Change it to an explicit "initWithContentsOfURL" method and then force a release by doing "data = nil;" when you are absolutely done with the image data.

Make UIWebView Like New On Each App Startup

Simple question that would be fantastic if somebody could answer.
I have a simple UIWebView used to log in to a certain service. However, when I reload the application, it stays logged in. What I need to be able to do is create the UIWebView like new each and every time the application starts. I'm not sure if there is an option somewhere in the interface builder, or perhaps a method of some kind I can place in the method that gets called when the app terminates, so that it releases the WebView or something.
What you need to do is not save the cache of the UIwebView. You can do this with a simple code like:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
Place the code in the UIwebview code.
Or in ApplicationDidFinishLoad (or something like that, haha) you can remove the cache with:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
Edit:
You've asked something in the comments. You can do something like the code below:
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.google.com"]];
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0.f, 0.f, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 20.f)];
[webView setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
//No Cache\\
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
//No Cache\\
webView.delegate = self;
[webView loadRequest:request];
[self.view addSubview:webView];
Ok,
Remove a specific request
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:NSURLRequest];
Or delete al cookies from the UIWebView
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if([[cookie domain] isEqualToString:someNSStringUrlDomain]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
Good luck,
Nathan
My answer will be in multiple parts to cover multiple bases.
1 - it's possible the app has a cookie set for the user so even when launching a new app the old cookie remains with the login details and therefore you will still be logged in.
To kill the cookies, try this before the uiwebview loads (ie in the viewwillload or other method)
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
2 - Is this on a multithreaded phone (iOS4 or newer on a 3GS or newer)? Are you sure the app is terminating as opposed to just going to background? If this is the case and you are just launching the previous app (where you were still logged in) you will need to hook into the - (void)applicationDidBecomeActive:(UIApplication *)application and remove the old web view and add a new one.

Clearing UIWebview cache

I have used UIWebview to load a web page using loadRequest: method, when I leave that scene I call [self.webView stopLoading]; and release the webView.
In activity monitor on first launch i have seen that the real memory increased by 4MB, and on multiple launches/loading the real memory doesn't increase. It is increasing only once.
I have checked the retain count of webview. It is proper i.e., 0. I think UIWebView is caching some data. How do I avoid caching or remove cached data? Or is there another reason for this?
I actually think it may retain cached information when you close out the UIWebView. I've tried removing a UIWebView from my UIViewController, releasing it, then creating a new one. The new one remembered exactly where I was at when I went back to an address without having to reload everything (it remembered my previous UIWebView was logged in).
So a couple of suggestions:
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:NSURLRequest];
This would remove a cached response for a specific request. There is also a call that will remove all cached responses for all requests ran on the UIWebView:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
After that, you can try deleting any associated cookies with the UIWebView:
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if([[cookie domain] isEqualToString:someNSStringUrlDomain]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
Swift 3:
// Remove all cache
URLCache.shared.removeAllCachedResponses()
// Delete any associated cookies
if let cookies = HTTPCookieStorage.shared.cookies {
for cookie in cookies {
HTTPCookieStorage.shared.deleteCookie(cookie)
}
}
Don't disable caching completely, it'll hurt your app performance and it's unnecessary. The important thing is to explicitly configure the cache at app startup and purge it when necessary.
So in application:DidFinishLaunchingWithOptions: configure the cache limits as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
int cacheSizeMemory = 4*1024*1024; // 4MB
int cacheSizeDisk = 32*1024*1024; // 32MB
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
// ... other launching code
}
Once you have it properly configured, then when you need to purge the cache (for example in applicationDidReceiveMemoryWarning or when you close a UIWebView) just do:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
and you'll see the memory is recovered. I blogged about this issue here: http://twobitlabs.com/2012/01/ios-ipad-iphone-nsurlcache-uiwebview-memory-utilization/
You can disable the caching by doing the following:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
[sharedCache release];
ARC:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
Swift 3.
// Remove all cache
URLCache.shared.removeAllCachedResponses()
// Delete any associated cookies
if let cookies = HTTPCookieStorage.shared.cookies {
for cookie in cookies {
HTTPCookieStorage.shared.deleteCookie(cookie)
}
}
I could not change the code, so I needed command line for testing purpose and figured this could help someone:
Application specific caches are stored in ~/Library/Caches/<bundle-identifier-of-your-app>, so simply remove as below and re-open your application
rm -rf ~/Library/Caches/com.mycompany.appname/
My educated guess is that the memory use you are seeing is not from the page content, but rather from loading UIWebView and all of it's supporting WebKit libraries. I love the UIWebView control, but it is a 'heavy' control that pulls in a very large block of code.
This code is a large sub-set of the iOS Safari browser, and likely initializes a large body of static structures.
After various attempt, only this works well for me (under ios 8):
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:1 diskCapacity:1 diskPath:nil];
[NSURLCache setSharedURLCache:cache];
For swift 2.0:
let cacheSizeMemory = 4*1024*1024; // 4MB
let cacheSizeDisk = 32*1024*1024; // 32MB
let sharedCache = NSURLCache(memoryCapacity: cacheSizeMemory, diskCapacity: cacheSizeDisk, diskPath: "nsurlcache")
NSURLCache.setSharedURLCache(sharedCache)
I am loading html pages from Documents and if they have the same name of css file UIWebView it seem it does not throw the previous css rules. Maybe because they have the same URL or something.
I tried this:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
[sharedCache release];
I tried this :
[[NSURLCache sharedURLCache] removeAllCachedResponses];
I am loading the initial page with:
NSURLRequest *appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
It refuses to throw its cached data! Frustrating!
I am doing this inside a PhoneGap (Cordova) application. I have not tried it in isolated UIWebView.
Update1: I have found this.
Changing the html files, though seems very messy.

Resources