UIImage imageNamed memory leak - ios

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.

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

Memory utilized never goes down using UIImage:imageWithContentsOfFile

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.

Instruments show memory leak - Xcode 5 / iOS7

I have the following piece of code:
NSString *bgImageName = [[Useful instance] getRootviewBackgroundImageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:bgImageName]];
imageView.clipsToBounds = YES;
CGRect rc = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[imageView setFrame:rc];
[self.view insertSubview:imageView atIndex:0];
[imageView release];
Instruments shows 100% and a memory leak at line two in the above code, which was not the case in xcode 4.6. I am working now with xCode 5 on osx 10.8.5
It seems, that i properly release the allocated UIImageView (line 7) which gets inserted in my view at line 6, so i can't see why intruments is bringing up a memory leak warning.
Does anybody know why instuments brings up that (in my opinion) wrong information?
EDIT: here's the instruments-screenshot with the allocation-summary of my leaked object:
The UIKit and QuartzCore are retaining my object, which is the reason for my leaking UIImageView (or am I wrong with this assumption?).
The UIImageView is inserted to the view (a UIViewController), which is referenced in my xib-file. How can control what happens with my added UIImageView after adding it to 'self.view'?
I had the same issue with Xcode 5 on iOS 7. After some experimentation I noticed that Instruments does not show a mem leak when running against the iOS 6.1 simulator or against a device (iPhone 5s) running iOS 7. Based on these I can only conclude that this is a false positive and a bug in the iOS 7 simulator.
EDIT: This issue no longer occurs after I updated to Xcode 5.0.1 and OS X Mavericks (I'm guessing it's the first that fixed it but can't tell for sure).
Instruments is showing the line of code that triggered the allocation of the leaked object and not why the object was actually leaked. Turn on reference count tracking and look at all the retain/release events on that image view. There'll be an extra retain (or a missing release).
I notice that this is happening on your RootViewController instance. Is this really the top level view controller (e.g. one that you never dismiss/pop)? If so, this Allocations Summary correctly telling you that the image view is still alive, and it's curious it's reported as a leak.
If the view controller has been dismissed, though, then you have a genuine leak (though the problem does not rest in the code in your question: you've done that correctly). Your Allocations Summary proves that your UIImageView is never getting destroyed. If the view controller was correctly dismissed and deallocated, the image view's Allocations Summary should look like:
Note, when I pop the view controller (28 seconds in my Profiling session), the view is getting deallocated, and thus the UIImageView is, too (see the two highlighted lines).
You can draw two conclusions from your Allocations Summary:
It doesn't show any other retains on your UIImageView suggests that this image view is not the problem at all. It is not the case that you accidentally over-retained this image view somewhere.
The fact that you don't see the removeFromSuperview, suggests that your superview, itself, is never getting deallocated.
So, the question is whether your RootViewController has been removed from the view controller hierarchy or not. If so, then you have a leak or retain cycle, probably of the view controller itself, not this image view.
By the way, it sounds like you're already familiar with these topics, but I often refer people to WWDC 2012 video iOS App Performance: Memory, which not only describes many memory issues, but also demonstrates the use of Instruments for tracking these down (e.g. notably heapshots).
I started having the same issues and I was looking into imageNamed. I found that in the past there were memory issues. I finally ended up using [[UIImage alloc] initWithContentsOfFile:actualPath] where actualPath comes from the main bundle.
I think #mishod has the correct answer.
I tested and yes UIImageView setImage indeed leaks!
If you cycle through a bunch of images with
[yourImageView setImage:[UIImage imageNamed:#"sampleImage.png"]];
you can see on instruments memory usage increasing.
This seems to be some kind of caching going around since after
cycling through all the images memory usage will go flat.
The correct, or at least, the non leaky way to do it is:
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"sampleImage" ofType:#"png"];
UIImage *newImage = [[UIImage alloc] initWithContentsOfFile:thePath];
[yourImageView setImage:newImage];
I verified this on my code as my APP was cycling through a lot
of large image files.
Just a suggestion. Don't run Instruments on simulator. Do it on a device. There's a lot of difference and you see better/relevant results. Eg. Simulator has a lot more memory than a real device may have.

UIImage being released but CGImage not being released

I appear to have a memory management issue when I create UIImage instances the CGImages are not being released.
I have a paging UIScrollView to scroll through a series of JPG images. Below is my entire class that is a page view in the paging scroll view.
The code is being run on the main thread. The code uses ARC. I have tried loading the images using imageWithContentsOfFile: (returns an autoreleased object) as well as initWithContentsOfFile:(returns a retained object). I have tried #autoreleasepool and using performSelectorOnMainThread to ensure that the code is being run on main thread as suggested in other posts.
When scrolling through the images memory usage just grows until the app gets terminated as illustrated in the screenshot from instruments. Note the high allocation to image io.
Screenshot showing virtual memory usage
In the following screenshot it can be seen that the GTGImageScrollerPageViews, UIImageViews and UIImages are being deallocated. Notice there are Transitory objects for these numbering in the high 300's. However the CGImages are not being released and the number of CGImages living is in the high 400's and 0 Transitory.
Screenshot showing allocations
EDIT: Previously I had been recycling and re-using GTGImageScrollerPageView instances in the ScrollView as is the common pattern for scrollviews like this. In order to simplify while trying to debug this problem I allow the entire GTGImageScrollerPageView to be deallocated after it has been displayed in the ScrollView. As you can see in the second image above, there are only 4 GTGImageScrollerPageView living and 377 transitory, there are also 388 UIImageViews and 389 UIIMages listed as transitory so it appears that the UIImageViews and UIImages are being deallocated fine.
If I manually release the CGImage with CGImageRelease (Commented out in the code below) the CGImages are released. I know that I should not do this because I do not own the CGImage but this is useful to verify that this is where the problem is occurring. The screenshots below show the same code tested in Instruments but with CGImageRelease uncommented.
Screenshot showing virtual memory usage with CGImageRelease used
Screenshot showing allocations with CGImageRelease used
In the profiling outputs where CGImageRelease is used, you can see that the correct number of CGImage objects are Living and Transitory and that memory does not grow unbounded. Furthermore the app does not crash during usage with CGImageRelease used.
If this were some system caching of CGImage then it should release the memory when a memory warning is raised, but it doesn't. Memory just continues to grow.
What here could be causing this unbounded growth in memory?
Here is the code for the page view
EDIT: In response to comments I have updated the code to simplify further thus eliminating distractions such as ivars and properties that are not needed to demonstrate the problem. The problem remains unchanged and the results are the same when profiling. I have also added in the NSLog which also outputs the thread. I do see the dealloc being called as expected on the GTGImageScrollerPageView and it is always thread #1 and the call to displayAspectThumbImage is always on thread #1.
I really dont believe that there is anything wrong with the code that is presented here and this is confirmed by the generous effort of Rob. Something else is causing this but all the code related to loading and displaying the image is here; there is no other code in effect here. The only other notable thing that I could think of raising is that the displayAspectThumbImage method is called from scrollViewDidScroll and scrollViewDidEndDecellerating methods of the scrollview delegate but the fact that the methods are called on the main thread should exclude autorelease issues due to being run on another thread.
I have checked and NSZombies is not enabled and there are no Zombies increasing my memory usage. Indeed when the CGImageRelease method is called the memory usage is pretty flat as can be seen in the screenshot above.
#implementation GTGImageScrollerPageView
- (void)dealloc {
NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSThread currentThread]);
}
- (void)displayAspectThumbImage:(NSString *)path {
NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSThread currentThread]);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self addSubview:imageView];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
[imageView setImage:image];
//If uncommented CGImages are disposed correctly
//CGImageRelease([imageView.image CGImage]);
}
#end
Tested on:
iOS 6.1.4
iOS 5.0.1
Really sorry to be answering this question myself but thought it would be useful to share my experience.
It turned out to be a category on UIImage that was part of a third party library I was using. I wont name the library as I have tested with the latest version and the problem is not there so there is nothing to gain by naming the library.
It is particularly strange as the library was not being used anywhere near the code that was leaking. It was only being used in one place in the whole project, but any time I used a UIImage it seems that it was affecting the UIImage instance. The mere presence of this category in the project was enough. This came as a real surprise.
The way I solved this was to begin by simulating the scenario in a new project and found that the code was not leaking there. I then migrated the code a little at a time and when I moved the categories across the leak appeared.
Huge thanks to Rob for his generous efforts to help me with this and even though he did not provide the solution directly, talking to him was really helpful in solving the problem. Nice to know there are such cool people out there.
I did a simple infinite scroller using your code and after scrolling through nearly 100 images the memory usage was, as one would have expected, thoroughly uneventful:
Looking at your source, I would have recommended a few little things (e.g. I would have put aspectImageView into a private class extension rather than the .h, I assume you're setting pageIndex from the calling routine but I'd add it as a parameter of displayAspectThumbImage, I would have made aspectImageView a weak property and refactor the code accordingly (e.g. create image view, add it as subview, then set the weak imageview property to point to this object), etc.), but none of those have direct bearing on your issue here.
Bottom line, your problem does not rest in the code you've shared with us.
Try to use property instead of ivar.
#property (nonatomic, strong) UIImageView *aspectImageView;

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.

Resources