Memory utilized never goes down using UIImage:imageWithContentsOfFile - ios

On my app i have several UIViewControllers to load multiple images on each one. Looking at the memory utilized i realized that the memory never goes down, as i present viewcontrollers and dimiss them, the memory always goes up. I started to search about and find out that the problem was the way i load images, the method i used was imageNamed:
UIImage *oneImage = [UIImage imageNamed:imageName];
So i changed the method to:
UIImage *oneImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
But the memory stills the same way...
I am using ARC and before the dismiss of the uivicontroller i call:
oneImage = nil;
And the problem persist, any idea of how to solve this?

The image probably goes inside the autorelease pool. I never understood this clearly about ARC and autorelease pools. I thought they became useless in ARC, useful only if you are mixing with old code, but it's not. Sometimes ARC release the resources even if you are using a method that returns an autorelease object right out of the scope and sometimes not. Use:
[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
There is an interesting article about this topic from Matt Galloway.

Related

Large amounts of memory allocated on setImage:

I have a UIImageView that was created programmatically ([[UIImageView alloc] init]). The app's memory stays in check until the setImage: method is called. Any thoughts?
I'm assuming you're setting your image to your image view using something like this:
[imgV setImage:[UIImage imageNamed:#"yourImg.png"]]
The problem with using that is that the app caches these images. If you'd like to avoid caching images, use imageWithContentsOfFile::
[imgV setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"yourImg.png" ofType:nil]]];
Also, be sure to set your image to nil when you're done using it:
[imgV setImage:nil];
I have had issues with this in the past, and here's some text from an email I got back from Apple in response to a TSI:
There are quite a few cases where you’re using the API UIImage
+imageNamed: to load images but you should be aware that imageNamed caches its image data even after the returned UIImage object is
released. Replacing calls to imageNamed with -imageWithContentsOfFile:
as outlined below is a way to ensure full control over your app’s
image data in memory

UIImage imageNamed memory leak

I am trying to understand why I have memory leaks in a very basic implementation of a UIImage and a UIImageView.
I am not using ARC in that case (correctly disabled).
My code is pretty straightforward :
UIImage *image = [UIImage imageNamed:#"my_image.jpg"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[[self view] addSubview:imageView];
[imageView release];
I am implementing this code in the viewDidLoad: method of a UIViewController.
By calling the method imageNamed: of UIImage, I know that I will get an object that I do not own / an autorelease object. This object will also be retained by the UIImageView object instantiated just after. So the only object I have the ownership is the UIImageView one.
After running this app with the Memory Leaks Instruments, I have this report :
I heard about the cache system that operates but I should not have have memory leaks because some datas are cached.
Here is a reference to the answer with the cache explanation :
https://stackoverflow.com/a/2930567/1154501
Thank you in advance !
Edit : Tried also with ARC, and I got the same issue.
[UIImage imageNamed:] is managed by the operating system. Deallocating the UIImage created from this method might free up the memory that was allocated. If you have lots of images or user generated content, you should be using [UIImage imageWithContentsOfFile:] or [UIImage imageWithData:].
If you create too many images with [UIImage imageNamed:], your app may get killed by iOS because of memory usage. I made a sample app to prove this to myself, see more here: iOS UIImage storage formats, memory usage and encoding / decoding
Did you try to open the 'Extended Detail' right panel and look for the exact line where the memory is leaking?
Your code is OK, my opinion is that the leak is somewhere else.

iOS memory usage increases until crash

I have an iOS application that uses a UINavigationController to move forwards (push) and backwards (pop) in a fairly simple master/detail fashion.
The detail view controller has 2 or 3 full screen images that I add manually using
layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];//layer1rect is a CGRect
UIImage *img1 = [[UIImage alloc]initWithContentsOfFile:imgName];//imgName is an image in the bundle
layer1.image = img1;
layer1.alpha = 1;
[self.view addSubview:layer1];
img1 = nil;
In my viewDidUnload I have
for (UIView *subview in [self.view subviews]) {
[subview removeFromSuperview];
}
layer1 = nil;
layer2 = nil;
layer3 = nil;
The problem I have is that after pushing and popping a dozen times, I get a memory warning and a crash.
I'm using ARC, though from what I've read elsewhere that's unlikely to be the source of the problem in itself.
When I run the app in instruments (on the device, rather than the simulator) I see some interesting results.
First of all, the memory allocation tool shows less than 2MB of memory usage and no leaks, right up to the warnings and the crash. I can also see that my instance of the detail view controller gets released when I do the pop.
However, if I look at the Activity Monitor I see the usage start at around 50MB and continue to increase every time I push to the detail view controller until it crashes at around 250MB.
I thought that maybe the images were being cached, but I'm not using imageName: anywhere, which is a common cause of caching.
So what am I missing? How can I ensure that when I pop the detail view controller off the stack that all of the memory it was using is made available again?
viewDidUnload is called if your controller's view is evicted from memory due to a low-memory warning. Dropping the view and loading it again later was how UIViewControllers dealt with low-memory warnings under iOS 2–5.
Under iOS 6 the view controller never drops its view. So you never get viewDidUnload. In your case that'll mean you add another UIImage every time the first block of code is running (I assume it's not in viewDidLoad?). The old one won't be deallocated because it has a superview; that you're releasing your reference to it makes no difference.
Furthermore, the initWithContentsOfFile: would be better expressed as [UIImage imageNamed:] as the latter explicitly caches the images whereas the former reloads from disk every time, creating a new copy of the pixel contents.
So the recommended changes are to change this:
layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];
To this:
[layer1 removeFromSuperview];
layer1 = [[UIImageView alloc] initWithFrame:layer1Rect];
If you have an existing layer1 in your view hierarchy that'll ensure it is deallocated when you implicitly release your reference to it on the initWithFrame: line.
Also:
UIImage *img1 = [[UIImage alloc]initWithContentsOfFile:imgName];
Would be better as:
UIImage *img1 = [UIImage imageNamed:imgName];
As then multiple uses of the image with that filename throughout your app will definitely all point to the same instance, but it'll still be removed from memory if warnings require it.
As for diagnosis, try turning on NSZombies. NSZombies causes deallocated objects to remain alive and is normally used so that you can catch uses of dangling pointers. In this case what you could do is turn on zombies and see whether it changes your memory footprint substantially. If not then that confirms that you're not actually deallocating things.
Activity Monitor is not necessarily a reliable measure — the iOS frameworks use NSCaches where appropriate and neither OSes bother taking memory away from a process if there's nobody to give it to, as that would just be a pointless expenditure of processing. You should use Instruments, especially as it can break down what is in memory by type and count, letting you know not just your total count but what you're spending memory on.
That's the same logic that explains why imageNamed: is usually a better choice despite your comments. Images you have previously loaded remain in memory for as long as there is spare memory to house them. They don't stay around if memory warnings are received and nobody is using them. Only if your responses to memory warnings are really expensive need it be a concern, and usually it helps avoid memory warnings by ensuring that reused resources have the same identity rather than being copies.

Instrument and leaks

I have an application and I am profiling it. I am quite new to instrument and I am quite new ios developer as well. I am working with ios6 and I have a very unusual leak. I create a category on UIImage and added helper methods to return the image for using capinsets. My category looks like this,
#implementation UIImage (Helpers)
+(UIImage*)resizableImageWithName:(NSString *)imageName andCapInsets:(UIEdgeInsets)insets{
UIImage *image = [UIImage imageNamed:imageName];
return [image resizableImageWithCapInsets:insets];
}
#end
The instrument shows 3/4 leaks in this area, the same place and I could not figure out the reason for it. Is it that, I have to release the new image I created inside the category, if I release it what am I going to return ? Could any one please explain the reason that I am leaking memory here.
And I used it like this;
[self.progressView setTrackImage:[UIImage resizableImageWithName:#"progress_bar_background.png" andCapInsets:UIEdgeInsetsMake(2, 2, 2, 2)]];
Is there something wrong in using this method in this way ?
The method that Instruments shows you is the place where the leaked memory is allocated -- not necessarily the place where the memory is leaked. Indeed your method is correct as to memory management.
Thus, you better inspect how you handle the returned UIImage object... possibly, if this hint does not help you finding the leak cause, post some more code.

UIImage initWithData behaves differently on iOS 4.3

I have an app that's been running fine in the App Store for 6 months or so, but none of its images (downloaded from a web site via "NSURLConnection initWithRequest") show up in iOS 4.3. It turns out that I have a line of code that looks like this:
UIImage *img = [[UIImage alloc] initWithData:data];
and shortly thereafter, to get ready for the next image, I do this:
data.length = 0;
When I comment out setting the length to zero (deferring it until the download of the next image begins), it works on iOS 4.3.
I've checked carefully to make sure there's no buffer interference, but each of my images is in its own instance of an object that has the buffer (data) as an instance variable.
It seems to me that in iOS 4.3 the initWithData implementation is not completely finished with the data when the method returns. Perhaps some work is being deferred until the UIImage is assigned to the UIImageView, which happens a bit later? Maybe even deferred until the UIImage is rendered?
Can anyone shed light on this seemingly-new behavior in iOS 4.3?
I think your analysis sounds about right. It's easy to imagine the kind of small change that would cause this, for example UIImage may have been changed to retain the data rather than copy it, or something like that. I'd file a bug with Apple to make sure that someone notices the issue, and then work around it by changing your code to:
UIImage *img = [[UIImage alloc] initWithData:[[data copy] autorelease]];
At least, I assume that'd work around it. It'd be very interesting if it doesn't... please let us know one way or the other.

Resources