Can C style blocks cause memory leaks? - objective-c

I'm working on a kiosk style slideshow app. I have a UIScrollView which shows the slides, and a factory class, which generates the slides. The "slides" themselves are UIViewController subclasses, which are loaded out from XIB files and customized by the factory class. In my main view controller, I set up the scroll view and start a timer. The timer calls a "reload" method every N seconds, which handles the reload and call to the factory class.
The method that the factory class uses looks something like this:
- (SlideViewController *)slideFromManagedObject:(Slide *)managedObject{
NSInteger slideType = [managedObject slideType];
switch(slideType){
case kSlideTypeA:
{
//
// configure arguments here
//
return [[SlideViewController alloc] initWithArgument:argument] autorelease];
break;
}
//
// More types here...
//
default:
break;
}
}
I haven't yet gotten to the point of defining all of my cases, but the ones that are filled out seem to cause jumps in memory usage. If I add return [[[UIViewController alloc] init] autorelease]; right before the switch/case, I get no visible view, as expected, but I also don't see those memory increases. I'm not sure, but I suspect that it's the "C blocks" that I'm wrapping my slide generation code in.
Some things to note:
When the app starts, I see the memory plateau from about 400 kilobytes to around double that. Then, when the slides progress, any of the slides whose generation code is contained in curly braces is called, the memory plateaus upwards again.
This behavior only seems to happen once per launch - when the app loops through all of the slides, the plateaus to_not_ happen again. However if the app is backgrounded and then relaunched, the plateaus do occur again, consuming even more memory.
When I left the app to run overnight, for about 10 hours and forty minutes, the memory usage had slowly climbed from about 1.44 megabytes to somewhere closer to 1.57 megabytes. I suspect that there are/were some other leaks in there that may have been fixed by my tweaking, but the main jump from about 800 kilobytes to somewhere between 1.4 and 1.5 megabytes is still an issue.
Instruments does not report any leaks, but the plateauing concerns me.
What could be causing the increased memory?
EDIT:
So I don't think it's the blocks, since using an if/else seems to do the same thing.
Here's a screenshot of the Allocations instrument running:
Where could possibly be holding on to these views?

One possible explanation for what you are seeing is some caching that UIKit (I assume) is doing of your objects (don't know what they are, but I think of images mostly).
Caching is often used during transitions and for other internalities of UIKit.
UIKit empties its caches usually when a memory warning is received, so you could try and send one to see what happens. In actuality, I suspect that results of sending a memory warning will not be very easy to analyze, since all of your views are also unloaded, hence memory will go down forcibly. But you can try...
As to how sending a memory warning to the device (as opposed to the simulator), here you find an useful S.O. post.

Related

Memory warning and crash (ARC) - how to identify why it's happening?

I've started to use the ARC recently and since then I blame it for every single memory problem. :) Perhaps, you could help me better understand what I'm doing wrong.
My current project is about CoreGraphics a lot - charts drawing, views filled with thumbnails and so on. I believe there would be no problem while using manual memory management, except maybe a few zombies... But as of now, application simply crashes every time I try to either create a lot of thumbnails or redraw a bit more complicated chart.
While profiling with Instruments I can see an awfully high value in resident memory as well as in the dirty one. Heap analysis shows rather alarming irregular grow...
When drawing just a few thumbnails, resident memory grows for about 200 MB. When everything is drawn, memory drops back on almost the same value as before drawing. However, with a lot of thumbnails, a value in resident memory is higher than 400 MB and that obviously crashes the app. I've tried to limit number of thumbnails drawn at the same time (NSOperationQueue and its maxConcurrentOperationCount), but as releasing so much memory seems to take a bit more time, it didn't really solve the issue.
Right now my app basically doesn't work as the real data works with a lot of complicated charts = lot of thumbnails.
Every thumbnail is created with this code I got from around here: (category on UIImage)
+ (void)beginImageContextWithSize:(CGSize)size
{
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
UIGraphicsBeginImageContextWithOptions(size, YES, 2.0);
} else {
UIGraphicsBeginImageContext(size);
}
} else {
UIGraphicsBeginImageContext(size);
}
}
+ (void)endImageContext
{
UIGraphicsEndImageContext();
}
+ (UIImage*)imageFromView:(UIView*)view
{
[self beginImageContextWithSize:[view bounds].size];
BOOL hidden = [view isHidden];
[view setHidden:NO];
[[view layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
[view setHidden:hidden];
return image;
}
+ (UIImage*)imageFromView:(UIView*)view scaledToSize:(CGSize)newSize
{
UIImage *image = [self imageFromView:view];
if ([view bounds].size.width != newSize.width ||
[view bounds].size.height != newSize.height) {
image = [self imageWithImage:image scaledToSize:newSize];
}
return image;
}
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
[self beginImageContextWithSize:newSize];
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[self endImageContext];
return newImage;
}
Is there some other way which wouldn't eat so much memory or is something really wrong with the code when using ARC?
The other place where memory warning + crash is happening is when there is too much redrawing of any view. It doesn't need to be quick, just many times. Memory stacks up until it crashes and I'm not able to find anything really responsible for it. (I can see a growing resident/dirty memory in VM Tracker and a heap growth in Allocations instrument)
My question basically is: how to find why it is even happening? My understanding is when there is no owner for given object, it's released ASAP. My inspection of code suggests a lot of objects are not released at all even though I don't see any reason for it to happen. I don't know about any retain cycles...
I've read through the Transitioning to ARC Release Notes, bbum's article about heap analysis and probably a dozen of others. Differs somehow heap analysis with and without ARC? I can't seem to do anything useful with its output.
Thank you for any ideas.
UPDATE: (to not force everybody read all the comments and to hold my promise)
By carefully getting through my code and adding #autoreleasepool, where it had any sense, memory consumption got lowered. The biggest problem was calling UIGraphicsBeginImageContext from background thread. After fixing it (see #Tammo Freese's answer for details) deallocation occurred soon enough to not crash an app.
My second crash (caused by many redrawing of the same chart), was completely solved by adding CGContextFlush(context) at the end of my drawing method. Shame on me.
A small warning for anyone trying to do something similar: use OpenGL. CoreGraphics is not quick enough for animating big drawings, especially not on an iPad 3. (first one with retina)
To answer your question: Identifying problems with memory warnings and crashes with ARC basically works like before with manual retain-release (MRR). ARC uses retain, release and autorelease just like MRR, it only inserts the calls for you, and has some optimizations in place that should even lower the memory consumption in some cases.
Regarding your problem:
In the screenshot of Instruments you posted, there are allocation spikes visible. In most cases I encountered so far, these spikes were caused by autoreleased objects hanging around too long.
You mentioned that you use NSOperationQueue. If you override -[NSOperationQueue main], make sure that you wrap the whole content of the method in #autoreleasepool { ... }. An autorelease pool may already be in place, but it is not guaranteed (and even if there is one, it may be around for longer than you think).
If 1. has not helped and you have a loop that processes the images, wrap the inner part of the loop in #autoreleasepool { ... } so that temporary objects are cleaned up immediately.
You mentioned that you use NSOperationQueue. Since iOS 4, drawing to a graphics context in UIKit is thread-safe, but if the documentation is right, UIGraphicsBeginImageContext should still only be called on the main thread! Update: The docs now state that since iOS 4, the function can be called from any thread, to the following is actually unnecessary! To be on the safe side, create the context with CGBitmapContextCreate and retrieve the image with CGBitmapContextCreateImage. Something along these lines:
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
// draw to the context here
CGImageRef newCGImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *result = [UIImage imageWithCGImage:newCGImage scale:scale orientation: UIImageOrientationUp];
CGImageRelease(newCGImage);
return result;
So, nothing you are doing relative to memory management (there is none!) looks improper. However, you mention using NSOperationQueue. Those UIGraphics... calls are marked as not thread safe, but others have stated they are as of iOS 4 (I cannot find a definitive answer to this, but recall that this is true.
In any case, you should not be calling these class methods from multiple threads. You could create a serial dispatch queue and feed all the work through that to insure single threaded usage.
What's missing here of course is what you do with the images after using them. Its possible you are retaining them in some way that is not obvious. Here are some tricks:
in any of your classes that use lots of images, add a dealloc() method that just logs its name and some identifier.
you can try to add a dealloc to UIImage to do the same.
try to drive your app using the simplest possible setup - fewest images etc - so you can verify that in fact that the images and their owners are getting dealloc'd.
when you want to make sure something is released, set the ivar or property to nil
I converted a 100 file project to ARC last summer and it worked perfectly out of the box. I have converted several open source projects to ARC as well with only one problem when I improperly used bridging. The technology is rock solid.
This is not an answer to your question but I was trying to solve similar problems long before ARC was introduced.
Recently I was working on an application that was caching images in memory and releasing them all after receiving memory warning. This worked fine as long as I was using the application at a normal speed (no crazy tapping). But when I started to generate a lot of events and many images started to load, the application did not manage to get the memory warning and it was crashing.
I once wrote a test application that was creating many autoreleased objects after tapping a button. I was able to tap faster (and create objects) than the OS managed to release the memory. The memory was slowly increasing so after a significant time or simply using bigger objects I would surely crash the application and cause device to reboot (looks really effective ;)). I checked that using Instruments which unfortunately affected the test and made everything slower but I suppose this is true also when not using Instruments.
On the other occasion I was working on a bigger project that is quite complex and has a lot of UI created from code. It also has a lot of string processing and nobody cared to use release - there were few thousands of autorelease calls when I checked last time. So after 5 minutes of slightly extensive usage of this application, it was crashing and rebooting the device.
If I'm correct then the OS/logic that is responsible for actually deallocating memory is not fast enough or has not high enough priority to save an application from crashing when a lot of memory operations are performed. I never confirmed these suspicions and I don't know how to solve this problem other than simply reducing allocated memory.

ARC: Memory does not get reclaimed?

I am working on an iPad (only) app and I stumbled across a weird problem. The app gets terminated after a memory warning on iPad 1 but works fine on iPad 2.
I am using ARC and targeting iOS 5. I use nibs and most of my assets are displayed using UIImageViews. I also have a few hundred buttons and plenty of gesture recognizers... I re-watched the WWDC11 videos (sessions 323 and 322) on ARC and I don't seem to be doing anything special.
The app is UIImage intensive, I am doing lots of animations using UIImage. I am however using the initWithContentsOfFile constructor rather than the imageNamed call. I'm trying to prevent the images from being cached by the system.
I'm also using GCD to schedule sound effects and to animate views. I'm always doing this on the main thread.
The app uses a UINavigationController that never has more than 1 UIViewController on it's stack. I can confirm that this is true because the didReceiveMemoryWarning only gets called on the current view controller (I'm logging the call).
The thing I don't understand is why Instruments is reporting high numbers (as if the view controllers don't get deallocated) in both the Allocations and VM Tracker instruments. The Allocations instrument shows a small drop when I navigate from one view controller to another (which is what I expect) but the VM Tracker Instrument shows that the Dirty Size is not dropping when I do the same thing. Eventually the app uses too much memory and gets terminated (on iPad 1). When I get memory warnings on the iPad 2 the app does NOT get terminated though...
It feels as if my images, sounds or views don't get destroyed and the memory does not get reclaimed... My object hierarchy is very basic and there should not be any retain cycles of any sort. I don't even have simple delegates...
Do you have any suggestions? I really don't want to release this app only for the iPad 2 or newer... It's an app for kids and it would be a pitty... I'd be so much happier to learn that I'm doing something wrong, as I really want to make sure this app is the best it can be...
Cheers,
Nick
There are ways to say, 'optimise' your objects by setting their properties to nil when certain things aren't needed -- so while you can't write a dealloc method anymore, you can do self.object = nil (when pertinent) which ends up doing something like this in a non-ARC world for an 'retain' (i.e., strong) property:
- (void)setObject:(id)newObject
{
[object release]; // send release message to current object
object = newObject; // set reference to newObject
[object retain]; // send retain message to newObject
}
Now while in ARC you don't/can't write retain/release yourself in your code, the compiler inserts these calls for you, meaning that in practise, setting a property to nil would do this in the above example:
[object release]; // send release message to current object
object = nil; // set reference to nil
[object retain]; // send retain message to nil (no effect)
Moreover, this is just the tip of the iceberg -- you should make sure that there are no retain cycles in your code, which could be resulting in objects leaking without recourse to their destruction. This means, that there may be places where you're using strong references to a property (i.e., an object), when you should be using a weak property. The difference being, that strong references are retained, and weak references are assigned, the former having its retainCount incremented and the latter resulting in a property assignment that looks like this if handwritten:
- (void)setObject:(id)newObject
{
object = newObject;
}
I don't like answering my own question but I figured it could be helpful to future googlers. I implemented my own UIImage based animation and I no longer use the animationImages property. Turns out my memory issues are now gone as I no longer need to store all the images in memory and I load them as they are required using a timer.
I actually think that rolling out a custom animation is beneficial since it allows for callbacks and more powerful customisation options.
Once I'm happy with it and I feel like it's ready to be shared I will post the class(es) on GitHub.

iOS (iPad) CoreGraphics & Foundation memory issues

I'm getting strange memory allocations from core iOS libraries, seen in Instruments Allocations (Malloc 1.00KB) which continually rise and result in app crash (iPad).
The app allows the user to browse through articles (body in webview, title/author/date in labels) - the problem occurring as each new view controller is loaded (previous view controllers are released/deallocating - this isn't the problem).
The following CoreGraphics allocations only happen when the .text property is set for the UILabel:
CoreGraphics "CGGLyphBitmapCreate", "CGFontSetStrikeValue" - sometimes one or both of these shows 6 or 7 times (1KB each) but at least one of each every time view loads.
In addition to this, Foundation "[NSNotificationCenter addObserver:selector:name:object:]" sometimes appears, sometimes not, sometimes up to 6/7 times on one load. I understand why adding observes this might allocate some memory, but surely deallocating view controller with "[NSNotificationCenter removeObserver:self] should remove all references again.
I've included screenshots from Instruments... won't even address the WebCore and libcache.dylib allocations that **sometimes show up on load. Very confused here too.
Did you track leaks? As long as it doesn't leak memory you normally don't have to worry.
Maybe you have a thread without an autorelease pool set up? If this is the case, and an autorelease object is created, e.g. by a getter method, the object cannot be retained by an autorelease pool since it does not exist, and it will leak.
So it your app is multithreaded, make sure that every thread has an #autoreleasepool{} block that covers most of the thread code.

memory too high in UINavigationController

i've a navigation controller with several viewController and tableViewController.
I'm using all autorelease objects, the problem is that when i push a new view and i use it, memory from Instrumets increase of a bigger value of memory released when i come back in the navigationController. There's not leak warning, when i return to the view then, memory will not increase again, it's like if was all in cache.
I've tried also using objects own by me, with alloc....and release but the story is the same. Is it normal?
UIViewControllers (or to be exact, views/resources/xibs associated with them) are interesting since iOS will hold on to the resources in memory and unload them as required. This is why often the dealloc method will seem like it's never called. However don't worry, if you did proper memory management, your controllers and resources will be freed up as required - if there's a low memory warning sent to your app, all the views that are hidden or have been popped, will be unloaded and dealloc'ed.
Try testing your app again, just like you did before, but this time emulate low memory usage (can be done in simulator from the Hardware menu), then see if memory usage drops due to your controllers being freed up by the system

CGImage, NSArray and Memory

This is a multiple part question, mostly because my ignorance on the matter has multiple layers.
First, I put together a caching system for caching CGImageRef objects. I keep it at the CGImageRef level (rather than UIImage) as I am loading images in background threads. When an image is loaded I put it into a NSMutableDictionary. I had to do a bit of arm twisting to get CGImageRef's into the array:
//Bunch of stuff drawing into a context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
[(id)imageRef autorelease];
[self.cache setObject:(id)imageRef forKey:#"SomeKey"];
So, as you can see, I'm trying to treat the Image Ref as an NSObject, setting it to autorelease then placing it in the dictionary. My expectation is this will allow the image to be cleaned up after being removed from the dictionary. Now, I am beginning to have my doubts.
My application clears the cache array when the user "restarts" to play with different images. Running the application in Instruments shows that the memory is not dropping back to the "start" level on restart, but instead remains steady. My gut tells me that when the array has all objects removed the CGImageRef is not being cleared.
However, I'm unable to confirm this as I don't quite know how to track down the actual source of the memory in instruments. It's just a list of (Malloc 16 Bytes, Malloc 32 Bytes, etc), drilling into them just show a list of dyld callers. Not sure how to properly read it.
So, first question, is my way of caching CGImageRef objects completely flawed? And is there a better way to confirm such things in instruments?
First of all, caching CGImages is OK and I don't see any problems with the code you posted.
Am I correctly assuming you use an NSMutableDictionary as the cache? If so, you can clear it by sending it -removeAllObjects, which should release all the keys and values. If you just set different images for the same keys, memory usage may remain roughly the same because you replace previous images with new ones. If the images have the same size, memory usage should be constant except brief spikes when you create a new batch of images.
As for Instruments, I've seen it both report false positives and miss real leaks. Try running it several times, making pauses, if possible, for the Leaks instrument to "catch up". This sounds crazy, but I think it may make it a bit more reliable.
If all else fails, you can log the contents of the cache before and after loading a set of images to make sure the cache itself works as expected.
Why not just cache UIImage objects; you can make them fine on a background thread?
It's UIImageView objects that you have to be more careful with and even they are OK for most operations in the background.