Your iPhone app seems to be working fine. But suddenly, it starts to run slow and crashes! You suspect it's memory-related. Here's how to track down and fix issues with retain cycles, which can cause memory not to be released properly.
I'm assuming you are using ARC and iOS5.0+. While ARC simplifies a lot of memory management, it won't spot all retain cycles for you!
First, check you're genuinely dealing with a memory condition. Try to reproduce the crash on a device, and look for crash reports using Xcode > Organiser > Device Logs.
Low memory crashes don't look like normal crash reports, you won't see a stack trace! Instead you'll probably just see Process = Unknown, and the crash report will contain a list of processes which were running at the time of the crash.
In this example, you can see that Biblegram was using over 25000 pages of memory. 1 page of memory is 4KB, so that's 100MB of memory, which seems much too high.
Next, fire up Instruments via Product > Profile and select the "Allocations" template. Playing around with the app, you should easily be able to locate places where the memory allocation keeps going up and up.
This "staircase" pattern is a giveaway. I'm repeatedly pushing and popping one view controller, but the memory goes up and up.
Try putting a breakpoint in the dealloc method of the problematic view controller. Most likely, this will never get hit, showing that the view controller is never released.
This is a strong indication that we have a retain cycle. When the view controller is dismissed there are still some strong references to it, so it doesn't get dealloced.
As one last verification, enter the name of the class into the search box in the top right of Instruments, and check the "# living" column. This is showing 3, when we'd expect it to show 1.
You can drill down into the instances of the view controller by tapping the small arrow to the right of the class name, and drill down further to see every place that the view controller is retained and released.
This view can be rather overwhelming: the system frameworks do a lot of retaining and releasing on your behalf! For example, instantiating a NIB can easily increase the retain count to 40 temporarily.
So: here are four common errors to look out for that may cause your retain count to be higher than expected.
1. NSTimer
If you create an NSTimer object on a view controller, be sure that invalidate is called on it when dismissing the view controller, otherwise it will retain self.
2. Observers/NSNotificationCenter
If you add an observer to NSNotificationCenter, make sure you remove all observers when dismissing the view controller, otherwise they will retain self.
3. Blocks
You should not call [self doSomething] from inside a block, this can easily lead to the block capturing a reference to self. Instead, make a weak reference to self:
BAD:
dispatch_async(queue, ^{ [self doSomething]; });
GOOD
__weak MyViewController *safeSelf = self; dispatch_async(queue, ^{ [safeSelf doSomething]; });
4. Delegates
if you use
someObj.delegate = self;
inside the view controller, check the delegate property on someObj is weak.
@property (nonatomic, weak) id delegate;
Once you've made your fixes, check that dealloc is getting hit and the allocations no longer increase endlessly.











