UIWebView taking lots of memory - ios

In my app when i load UIWebView with any Website url the memory jumps from 30mb to around 140mb.
I am using ARC
and when dismissing the UIWebViewController[Viewcontroller which contains UIWebView], it doesnt releases the memory.
Can any body help me how to solve this memory issues as well as please also provide me pointers of memory bestpractices in ARC
For loading the webpage :-
NSURL *nsurl=[NSURL URLWithString:self.url];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[webview loadRequest:nsrequest];
FOr Dismissing the Viewcontroller:-
[self dismissViewControllerAnimated:YES completion:^{
webview.delegate=nil;
webview=nil;
}];
Thanks in Advance :)

wow, today we also meet this problem. when we try to load a web page which contain lots pictures or a huge web page, it's easy to crash. we also find the memory used nearly 200M.
finally we find that we could remove the memory cache of web view
[[NSURLCache sharedURLCache] removeAllCachedResponses];
you can try it when you receive memory warning. or you can call it in the dealloc method.
if it still have some problems, try to limit the memory cache size by call
[[NSURLCache sharedURLCache] setMemoryCapacity:size]
good luck!

Don't listen to them,
Its not a problem, phones can handle all that memory usage, try using an MKMapView and watch your memory usage soar.
In your simulator, try putting through a low memory warning cmd+shift+m, and see what happens, you can't expect all web views to run at the same memory usage, also, you have give a chance for your phone to buffer it all out.
iPhones can handle all of that, so you shouldn't worry much about it.
But try this anyway:
The behaviour of UIWebViews changes if you uncheck "Detects Links" in the nib file, so try toggling that and see what happens.

Use Instruments Allocation tab to see the number of instances of your UIWebViewController live in memory. (Cmd + I in Xcode, let Instruments open, select Allocations, and type UIWebViewController in the search bar)
If there's more than what you expect it to be, lookout for a retain cycle.
Try to use weak references within blocks instead of self.
Make sure you aren't holding a strong reference to the UIWebViewController object at
more than one place.
Put a breakpoint/NSLog in the dealloc method your viewController to see if it is being called.

There is no sense to search for a problem until you are sure that problem exists.
There are two possibilities why memory usage is so high:
1.UIWebView is not deallocated.
2.It is some system caching or some specific iOS memory processing algorithm.
In second case you can do nothing. To check first case you can try to use instruments or just subclass UIWebView and add logging to dealloc method.
#implementation MyUIWebView
- (void)dealloc
{
NSLog(#"MyUIWebView deallocated!");
}
#end
Do not forget to change UIWebView to MyUIWebView in your code.
And only if you will be sure that UIWebView is not deallocated you should start to search for strong pointers, retain circles and so on.

add
if (webView.isLoading){
webView.stopLoading;
}
webView.delegate=nil;
before this line
[self dismissViewControllerAnimated:YES completion...]
nil ing your webView should be taken care of by ARC. Don't refer to the web view in the completion block.

I think you forgot to remove the UIWebView from the view hierarchy. This would cause an extra retain count and prevent the memory from being reclaimed when you nil'd out the property.

Just some things you should check:
A controller has a strong reference to its view, and this view retains all the subviews as long as you keep them inside the view hierarchy, so any additional properties should be weak, including your webView property.
When using ARC there's no need to "nil" everything. ARC will nil all its strong references at dealloc automatically.
No need to do anything with weak references neither. Moreover on iOS 5+ weak references become nil automatically if the referenced object gets deallocated somewhere else.
I think that most likely your whole view controller is getting retained somewhere, and thus its view and the webView.
Make sure the you only keep weak references to the controller and let it be retained by a container or presenting controller so it gets released automatically as long as it gets popped or dismissed.

I was having the same problem in my application. I would recommend that you should use WKWebView. WKWebView was introduced with iOS 8.
You can read more about it here
apple documentation
The memory issue is definitely solved. You will get all the functionality from webview and some more(i.e. progress). Hope this helps

If you want to reduce the memory usage of UIWerview immediately, you can try:
1.
[webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
or
2.
[webview loadHTMLString: #"" baseURL: nil];
or
3.
[webview loadData:nil MIMEType:nil textEncodingName:nil baseURL:nil];
It worked to me.

Related

How to find the real cause of memory warning and how to resolve in iOS app

I have go through many posts related memory management, ARC , memory management techniques like autoreleasepool and using instruments tool to detect which code is causing memory warning but in my case I am not able to figure out the exact cause.
Basic details which you must know about the app:
We have developed an iPad app. In that we have to use more then 2000 images in some case, so when my app launch we do not want to show them placeholder image (client requirement).so to achieve this, we used SDWebImage , store images on disk and later than we are loading images from there.
There are so many core animation I have performed like "Gennie effect", display pop-up and so many other core animations.
We have used ARC in our project and we found that due to memory warning app is crashing randomly.
We have used Instruments "Allocation" for finding the dirty memory.
Previously we analyze the logs and we stored images with SDWebImage in DISK, it resolve that frequently crashing of app, but still app is crashing due to memory warning.
When we go deep in that we found "Anonymous VM" is keep incrasing and not releasing memory when any screen switch in iPad.
Here is the screenshot of profiling of our app on device.
Anyone please suggest tips or coding techniques or any idea by which We can reduce memory load and resolve memory warning.
Any help will be appreciated. Thanks.
In WWDC 2012 Session 242 iOS App Performance: Memory,Apple introduce a way to detect memory problem with Allocations template of Instruments,start from 31 minutes.
1.
Apple suggests push pop repeatedly before take a snapshot of the heap,I prefer push pop just once.
The Snapshot button is Named "Mark Generation"
You should take Snapshot multiple times.
2.
If your memory grow between each Snapshot,You can dig into one of the snapshot except the first one.
Sort the objects by Persitent,you probably has seen your problem ViewController (if you only push pop once).Or you can just search for it.
3.Now you can dig into the call tree ,find and fix the problems.
4.You can use this technique not just for push pop viewControllers,but also scrolling in a tableView ,perform a databaseSearch and any other cases.
Wolverine,
Not saying this is an absolute answer to your question, but might provide you some hint to resolve the problem.
My Case :
Today morning while debugging the memory crash just as your's in one of my chat application I came accross a similar scenario. I am using SDWebImage as well to cache and load the images for the subsequent usages.
Initial observations made me believe that it is the crash because of SDWebImage. Soon I realized that crach was rather because of a very simple issue.
I had never tested whether my viewController's dealloc is getting called or not. On putting a breakpoint in dealloc I realized dealloc was never being called (for various reasons which I resolved now).
As in your case, my chatViewController though not loading 100's of images used to load 8 - 10 images at a time and crash started appearing when I used panorama images rather then normal Images.
Conclusion :
My viewController had strong reference to UIImageView which was loading heavy images and as dealloc was never called for my ViewController all the loaded images were never realesed which resulted in memory crash.
As I was loading heavy images I was performing several calculations to reduce the size of image. Most of the calculations involved repeated creation of image from NSData using UIImageJPEGRepresentation. Repeated allocation of data and image holder variable added to memory consumption of the app.
As my ViewController involved number of API calls, I had created a singleton reference service class to which I used to pass complitionblocks and unknwingly I had used self inside the completionblocks passed to the methods. Which actually contributed to ARC reference count increment and never let my view controller to get deallocate.
Way to Debug
As you have already mentioned in your question that you are loading loads of images in your ViewController make sure the viewController gets deallocated properly and releases all the memory loaded by all the ImageView's present in ViewController.
If your ViewController's dealloc is not getting called then as just a way to test (remember not a solution) in viewWillAppear set all the imageView's image property to nil and forcefully clear the memory loaded.
If ViewController dealloc not getting called turns out to be the culprit, try finding where you are sending self as strong reference. If found try using weak self.
I know its not a absolute answer to your question. Just sharing my experience with you. Hope this will atleast give you hint to solve yours.
Try Infer it may helpfull, it reports memory leak problems in iOS and C code.
A static analyzer in deployment at Facebook, where it is used as part of the development process for mobile apps. Infer targets critical bugs such as null pointer exceptions, resource leaks and memory leaks — problems which lead to crashes or performance degradation in apps.
It looks like some object in your app is not being released, probably due to a retain cycle. Check all references to delegates are weak references.
Also check blocks and ensure a strong reference to self is not captured in a block. If Object A keeps a strong reference to Object B, then passes a block containing a strong reference to self to object B, both objects are potentially locked in a retain cycle.
Use this syntax to pass a weak reference to self:
__weak typeof(self)weakSelf = self;
[doSomethingWithBlock:^() {
__strong typeof(weakSelf)strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[strongSelf doSomething];}];
in Swift do this:
someObject.doSomething() { [weak self] in
self?.doSomething()
}
or use [unowned self] - both create a weak reference to self, in the case of [weak self], self is optional
To ensure all your objects are deallocated as expected, put a log statement in your dealloc / deinit functions and check they are really being called.

Memory Issue with ARC

I've used ARC to build a fairly simple application. However, I'm running into a memory deficiency, but I can't figure out what is causing it. Since I can't clarify what is causing it, I have a few specific details and questions.
The problem ensues when I try to load a new View Controller. This view controller hosts a number of images and, when loaded, will add a 3-4 minute audio file to an AVAudioPlayer in a singleton class I have.
The problem occurs when the view controller is pushed and popped 8-10 times. When the view controller is popped, I call stop on the AVAudioPlayer and return all relevant objects (including the AVAudioPlayer instance) back to nil.
I don't really understand what could be causing the memory leak, or what else could be ravaging the device memory, but I do have a few specific questions.
When stopping the AVAudioPlayer, will that still allow for a proper release in the memory?
Will setting the AVAudioPlayer pointer to nil after calling stop prevent the system from releasing certain data from the device memory?
Shouldn't anything, in ARC, be released when the owner(s) are deallocated (I'm asking about all of the views and data in my UIViewController that gets popped off the stack)?
Are there any issues with AVFoundation or AVAudioPlayer in ARC that I should know?
Is calling stop the wrong way to end an audio session / have it be released?
EDIT: I have started using the instruments tool in order to track my allocations and leaks. There aren't any memory leaks, or so the tool says, but the application will crash, nearly regardless of the live bytes. The application will crash when the total RAM used is over 200MB (210-230MB - my device has 256MB of RAM). My new question is will the total bytes allocated (even if they're not live) affect the memory crashes? If so, how can I prevent this?
Here is an image of a run that crashed. You can see the clump of memory warnings at the end.
Q) The problem occurs when the view controller is pushed and popped 8-10 times. When the view controller is popped, I call stop on the AVAudioPlayer and return all relevant objects (including the AVAudioPlayer instance) back to nil.
A) Add a dealloc method and log the dealloc, so you know nothing is retaining those, then look for these in the console:
- (void)dealloc
{
NSLog(#"MySpecialViewController getting dealloced!");
}
if you don't see these something is retaining this object - a delegate possibly.
Q) I don't really understand what could be causing the memory leak, or what else could be ravaging the device memory, but I do have a few specific questions.
A) You should be using ObjectAlloc in Instruments (and Leaks). These are really easy to use - run the project with them in the Simulator, just use the defaults, and you'll get a lot of good info right away. You can see WHAT is leaking which should really help.
Q) When stopping the AVAudioPlayer, will that still allow for a proper release in the memory?
A) No. You have to set your strong ivar/property to nil (which you said you are doing). I just checked and the AVAudioPlayer does not retain the delegate. That said, as a general rule, when I am shutting down anything its:
[something stop/cancel/etc];
something.delegate = nil;
something = nil;
Will setting the AVAudioPlayer pointer to nil after calling stop prevent the system from releasing certain data from the device memory?
Q) Shouldn't anything, in ARC, be released when the owner(s) are deallocated (I'm asking about all of the views and data in my UIViewController that gets popped off the stack)?
A) If the only thing that is retaining the ViewController, for instance the navigationController's viewControllers array, then yes, your viewController subclass should get dealloced, and all strong properties and ivars will too.
Q) Are there any issues with AVFoundation or AVAudioPlayer in ARC that I should know?
A) I know of none, and since this is a popular class expect your problem is in your code.
Q) Is calling stop the wrong way to end an audio session / have it be released?
A) If it were me:
[avPlayer stop];
avPlayer.delegate = nil;
avPlayer = nil;
EDIT: If you are getting memory warnings when your app is running, you are leaking or consuming some huge amounts of memory. Put a breakpoint where you get this warning, stop your app, and look at your allocations. I've never gotten a memory warning for real (I use ARC exclusively)
One area where it is easy to run into memory trouble while using ARC is situations where you are interacting with a Cocoa Touch API that requires the main thread from a background thread.
It doesn't sound like you are up against this, but if you start seeing inconsistent behavior (e.g. what appear to be random crashes, objects going out of scope prematurely, etc.) it is worth investing some effort to see if you are interacting with Cocoa Touch from a background thread.
Sometimes callbacks from Cocoa Touch (that one might assume would be on the main thread) aren't (we have seen this in some Game Center API's).

Reusing UIWebView is causing crashes

I've read that reusing UIWebViews is kind of a bad practice. Some code I inherited at work is pushing a variety of content to a UIWebView. Powerpoints, Word documents, and video. This all works just fine under normal circumstances. When we get to switching out the content in the UIWebView too fast, it dumps.
My webView is set as a property. It is hooked up in IB just fine. Normal selection from our tableView loads local content just fine. It takes rapid fire selection of either the same cell or combinations of multiple to get it to crash.
I can capture some error messages for it for the webViewDidFailWithError. But those will trigger even without a crash.
Here is the error localized string.
The operation couldn’t be completed. (NSURLErrorDomain error -999.)
When the app does finally crash, it blows up on this goofy WebCore error.
If anyone has any links or some code examples how to handle this I would appreciate it. Maybe an example how to best reuse my webView property without blowing things up.
I would load some of my code, but there is a lot going on not related to the webView itself. All content being pushed to the webView is done via [self.webView loadRequest:request]; with the request being an NSURLRequest filled with the path to the local content.
I will be very appreciative if anyone can help me out on this one. Fingers crossed for something simple.
I'm not certain this way is the "best" way to solve the issue, but it does seem to be working quite well. Short, sweet, and it works.
I disabled userInteraction with the tableView that updates the content in the webView. Since you have to mash on it so much, missing a tap here or there probably won't be missed. So for now, this is the fix.
#pragma mark -
#pragma mark UIWebViewDelegate methods
-(void)webViewDidStartLoad:(UIWebView *)webView {
[self.tableView setUserInteractionEnabled:NO];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView {
[self.tableView setUserInteractionEnabled:YES];
}
// I re-enable on load failures as they can block you out entirely on fail
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
[self.tableView setUserInteractionEnabled:YES];
}
If it’s crashing only when you tap quickly, could you put a gesture recognizer over it to act as a poor man's rate limiter?
Are you using [webview stopLoading]; before loading another request? What you might need to do is cancel or stop the current loading before trying to load a different one. The other option being restrict the user input.
In your UIWebViewDelegate, you could implement webView:shouldStartLoadWithRequest:navigationType: to return NO if there is already a request loading.
I can't help but think you'd be better off not re-using the UIWebView. Ditch the nib, create it programmatically and set it to nil and re-create/re-assign/re-alloc it when the data source changes.
On a different note I would make sure to use NSOperationQueue for the data loading.

Invisible memory leaks... iOS 4

So I've got an issue with memory. It seems that I am not properly releasing or deallocating objects because I am getting this subtle buildup of memory in my game.
I init all objects like this:
self = [super init];
if (self != nil) {
//inititalize object
}
I release all objects in the dealloc methods like so:
[object release], object = nil;
Now I feel I should mention that my game runs on views. I have the main view that loads the sub-views into one variable called 'currentView'. I switch between the views like so.
if (currentView != nil) {
[currentView dealloc];
}
currentView = [[newView alloc] initWithVariables:vars];
I want to know if the issue is with my initialization and deallocation of objects or if its the views. I also want to know any strategies I can use to track down any leaks that don't show up in instruments.
I need this information ASAP, and if you can help it would be REALLY appreciated. Thanks.
EDIT: When I run it through instruments it shows the memory my app uses. and it has a strange pattern. When it loads the first view it starts with approx. 17mb used. Then I switch to the second view and it goes up to 22mb. Now if I go back to the first view it DOESN'T go down to 17mb again but down to about 20mb instead. Now when I continue to switch between the two it goes between 22mb and 20mb and stays pretty much the same. How can this be explained?
EDIT2: The pattern explained in the above edit is consistent throughout the entire game. Because of this consistency I've noticed a 70kb per-level leak in my game. Obviously this should not cause any problems while my game stays under 100mb so unless they play my game for EXTENDED periods of time, this shouldn't be a problem.
Although, I would still like to know how I can track down that 70kb leak.
You should be using release instead of dealloc. Every call to alloc should be matched with a release or autorelease; in your case, release because you want to control when it's freed.

Prevent UIWebView allocating to much memory

I have a problem with UIWebView. I am using this component for loading normal web pages on iPad/iPhone. UIWebView is presented in modalViewController. Some pages are very memory hungry and some also have Flash content.
The problem is that sometimes on some pages I get memory warning
Received memory warning. Level=1
When this happens and when I close this modalViewController (which has page loaded in webView) than the previous view reloads itself automacially - method viewDidLoad is fired again.
I suspect that some pages in UIWebView consume to much memory and than application release memory of its views, but does not crash.
Is there a way to limit memory consumption of webView or is there any other way to avoid this memory warning?
Thanks!
You have very little control of the memory usage of UIWebView objects. You also have very little control of the overall system memory usage. So there is no way to avoid memory warnings. iOS expects your apps to behave properly when receiving memory warnings, so your viewDidLoad method should be written to handle rerunning after a memory warning.
The only things you can do to limit the memory usage of a UIWebView is to have it view simple web pages. It looks like from your question, that's not an option.
Edit: viewDidUnload is called during low-memory conditions. This method is expected to free up anything that's easy to recreate in the viewDidLoad method. Don't release the state information you want to show the user when this view is returned to the screen. Then in your viewDidLoad method check all the objects you create. If they are nil, they need to be initialized, otherwise this isn't the first time your viewDidLoad method was called, and you shouldn't initialize your objects agin.
In other words: viewDidUnload handles low-memory situations and viewDidLoad should not assume it is run once.

Resources