UIImage being released but CGImage not being released - ios

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;

Related

iOS: Create UIImage from the background thread?

Apples documentation says:
Because image objects are immutable, you cannot change their properties after creation. Most image properties are set automatically using metadata in the accompanying image file or image data. The immutable nature of image objects also means that they are safe to create and use from any thread.
Link
Also if you look at the answers of this "Thread safety of UIImage" question, it is concluded that it is safe to use them from any thread (at least since iOS 9).
Yet, there are comments that complain about issues, especially about creating UIImages on a background thread.
In my case I'm sure that this leads an issue where animations stop working.
Does anybody have insights on this?
I can't see your code but I suspect this is a UIImage vs UIImageView issue if you're experiencing animation problems.
A UIImage manages image data off screen.
A UIImageView displays your image on to your user interface.
Handling image data off the main thread with UIImage is fine.
Displaying or animating an image with UIImageView is not.

SDWebImage - Memory leak in UITableView?

I'm sorry in advance as maybe this is a dumb and noob question...
I'm using SDWebImage to display pictures in a UITableView in my cellForRowAtIndexPath method, using the classic
[cell.pointPicture setImageWithURL:[NSURL URLWithString:thePoint.imageURL] placeholderImage:[UIImage imageNamed:POINT_DEFAULT_IMAGE]];
(the displayed pictures are light and well compressed jpgs, just some ko, and yes I'm using dequeueReusableCellWithIdentifier of course).
When I inspect my app with "Instrument - Allocations", and just scroll down my UITableView (with 40 cells containing picture, a bit like Instagram), I got a huge amount of memory used ! (see screenshot)
But it seems to be "VM", and especially "VM: CG raster data" from the coreGraphics library.
So the questions are :
Is it normal?
Is that a serious problem?
Is there a way to avoid this?
I'm sorry but after few search on the web I can't find any relevant information concerning the "VM: CG raster data"... Any idea? Thanks in advance !
I experienced the same issue and found the root cause, at least in my implementation.
Root Cause
The root cause was that my table cell stored a strong pointer to the image which is stored in SDWebImage cache. This strong pointer, caused the memory release function of SDWebImage removeAllObjects not to release any memory when receiving a memory warning from iOS.
Solution 1 - Keep weak pointers from within your ViewController and allow only SDWebImage to keep a strong pointer to all UIImage objects.
Solution 2 - Implement - (void)prepareForReuse and set the image pointers to nil
To test this solution run your application and simulate a memory warning - You will be able to see the data removed
I had the same issue. My problem seemed to originate from something else than the SDWebImage cache but since it did contribute to the memory buildup, the first thing I though was that the cache could be the cause of my problem. But it wasn't. It could be you have the same kind of issue. Keep in mind that I am using ARC.
Run the profiler with the Leaks template and check for the allocation of your own classes in the Allocation Summary.
Dive into them and check how they are allocated, if there are leaks. Mind that a leak doesn't show up in the Leaks instrument because you are using ARC. So Instruments could think everything is going oké but there still could be a leak somewhere. By diving in to the allocation of your own classes you could figure out what is going wrong.
Keep in mind that the retain/release count information is only provided when using the Leaks template and not while using the Allocations template.
My problem was that I was referencing instance variables and self directly from within blocks without reassigning them to __weak variables. When self is used within a block it will automatically be retained by ARC and sometimes never released. A weak reference prevents that from happening.
For example, this is wrong:
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardDidShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[self.view setContentOffset:CGPointMake(0.0, kKeyboardOffset) animated:YES];
}];
You should call self using a __weak reference like this:
__weak YourViewControllerClass *weakSelf = self;
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardDidShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
[weakSelf.view setContentOffset:CGPointMake(0.0, kKeyboardOffset) animated:YES];
}];
Since my app uses a lot of block's I had a ton of leaks the Leaks instrument could not detect. When I fixed them the memory problem was gone.
I hope this helps.
As explained to me by someone working on SDWebImage :
SDWebImage cache images is using NSCache. It is discardable memory. See Apple documentation, so this is a perfectly normal behavior and the memory is released if needed.
Although NSCache will free memory on low memory system notifications also more critical (and way smaller in memory footprint) objects will get discarded in other parts of the program.
It would be better to set the library's maxMemoryCost (which sets the NSCache totalCostLimit) to a limit to prevent SDWebImage from triggering memory notifications.

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.

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.

Releasing memory in iOS 5 under arc

I know this question asked many times, but I have not yet found solution. My app goes between views in this order a->b->c->d->b. This means that from view "d" I don't always go back, and the needs to jump to view "b". The problem is that memory of views "c" and "d" is not release and after some loops (b->c->d->b->c....) the app crashes. It is very important the order of the operations. I have to mention that I navigate between views with modal segue. I have tried releasing memory in many ways:
Putting in DidRecieveMemoryWarning: _myProperty = nil;
Putting in ViewDidUnload: _myProperty = nil;
Changing all properties to weak.
Wrapping code with #autoreleasepool.
Nothing helped, the app crashes after awhile, how can release views and memory by "force"?
ARC was made for not releasing by force letting the OS handle all releases. You should use xcode instruments to find out how much memory your app is using. It sounds to me like you bloat your memory so it will be best for you to check how much memory is being used and what can you do to optimize it so allocation capacities will stay in an acceptable limits. (e.g. Loading 1000 images in a view where each image is 1Mb in size is a total waste and will probably cause such crash. This example is intentionally exaggerated so you'll get the idea)
Objective-C (primarily) uses a reference counting memory model. There's manual reference counting and Automatic Reference Counting (ARC).
How ARC Works
If you've ever worked with manual reference counting and run the clang static analyzer tool, you'll see that it does an amazing job of finding memory leaks. This is the idea behind ARC - if the analyzer can do such a good job of finding leaks, then why not let it perform memory management?
So, the analysis is done at compile time, and the resultant code is instrumented with retain and release calls. This is the foundation of ARC and its a great memory model, giving a good balance between performance and ease of use. Its especially useful in mobile computing, because, unlike garbage collection it won't require any additional CPU cycles or battery drain.
(NB: ARC also introduces weak references, which is a way of nulling out dangling pointers at runtime).
Using ARC is not quite as simple as garbage collection - you still need to know how the reference counting memory model works. The following problems can occur.
Retain Cycles
The main cause of a memory leak is a retain cycle. This is where object A has a reference to Object B, and Object B has a reference to Object A. For example an AirlineFlight contains a Passenger and the Passenger has an AirlineFlight -> memory leak.
This is very common in Cocoa. Another example is a ViewController owns a view, and the View has a delegate, which is the ViewController. Sound familiar?
To avoid the retain cycle, its necessary for one of the objects to not retain the other. The most common approach is to designate one as the parent, and the other as the child. The parent retains the child, but the child does not retain the parent. Example:
View Controller
#interface MyViewController
#property (nonatomic, strong) MyView* view
#end
View
#interface MyView
#property (nonatomic, assign) MyViewController* controller
#end
The retain-cycle is now broken and the memory leak will no longer occur.
Dangling Pointers
The above example no longer has a memory leak, but can lead to another problem - dangling pointers. The view controller might go away, while the view still has a reference to the controller. Any call to the controller will now cause a crash.
There's two ways to fix this:
Nil out the reference to the controller in the dealloc method. . . but probably just
Use a weak reference, as follows:
#property (nonatomic, weak) MyViewController* controller
iOS 5 and above (or OSX 10.7) have a little runtime utility that keeps track of dangling pointers and nils them out for you - these are called weak references.
Finding memory Leaks
There are several ways.
Use the clang static analyzer to detect retain cycles.
Use the Instruments tool
I like to put log statements in the dealloc method of my controllers and other key classes to ensure they being released.
Finding Dangling Pointers
Compile with the environment variable NS_ZOMBIES_ENABLED (or edit the Scheme in Xcode and click the checkbox, which will set this parameter for you).
Auto-release Pools
Another problem that can occur is too many objects being allocated within a run-loop. . To correct this you can create another auto release pool, within the main pool.
#autoreleasepool
{
}
(In your case, I don't think this is the problem).
I'v also come across this problem, but I was writing a cocoa app for OSX10.8, not iOS.
I found that the app memory usage will increase all the time when I do something like showing a new window then closing again and again. It seems like the window I see is a new one every time. 10MB->20MB->30MB...
Finally, I directly double-clicked the .app file, and checked the activity monitor again. The fact was that it really came to be normal. The app ran in release mode is the same situation. So I suppose ARC will not release memory immediately in debug mode if you run a app via Xcode Run.
Are you using heavy png images? I had this same problem a while ago and a height x width image uses height x width x 4 of RAM (since it is shared with GPU). Only my background wasted 120MB approx. If it is the case try to redimension your images.

Resources