Not all "leaks" show up in the "Leaks" tool in Instruments. And, notably, if a view controller has a strong reference cycle, not only will the view controller not be deallocated, but none of its members will be deallocated either. But looking at your allocations your memory is never getting released, so you probably do have a leak somewhere. It's hard for us to diagnose, though, because your github project is incomplete. But here are a few thoughts:
Strong reference cycles don't always show up in the Leaks tool.
In a variation of the traditional strong reference cycle, your code employs a repeating NSTimer
that will keep a strong reference to your view controller which will result in your view controller never being released (because the timer maintains its own strong reference to your view controller). To fix this, your view controller must stop the timer when the associated view disappears:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.timer = [NSTimer scheduledTimerWithTimeInterval: 0.05f target: self selector: @selector(tick) userInfo: nil repeats: YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
Besides of strong reference cycles, like above, another phenomenon that can result in increasing allocations like you've shared with us is a circular flow between view controllers. For example, if your app does a push/modal segue from view controller A to view controller B, the app must then pop/dismiss/unwind back to view controller A. If you push/modal from B to a new instance of A, you'll end up abandoning your old instance of A, resulting in an allocations graph like yours.
These are just a few examples of the sorts of things that can result in your allocations graph. But it's hard for us to diagnose further with the limited information provided.
Before doing anything else, use Xcode's static analyzer (command+shift+B or "Analyze" on the "Product" menu) and make sure you get a clean bill of health there. Let Xcode help you identify your programming issues in your code.
Once you've resolved any issues identified by the static analyzer, you can then dive into Instruments. Refer to the WWDC 2012 video, iOS App Performance: Memory. About 32 minutes into it, it shows an allocation graph much like yours, describes the three sources of those sorts of issues (leaks, abandoned memory, or cached memory), and shows you how to use the Allocations tool to identify the precise source of the problem.
You should follow along that video and you'll definitely gain some familiarity with Allocations tool features (such as comparing heap snapshots) to identify what object leaked, or look at the extended details and call tree to find the source code that created the leaked object. Once you identify precisely what's leaking, we can help you resolve the issue.
By the way, even easier than the heapshots described in that video, I'll often just option-click-and-drag at a particular spike (notably one that obviously is never released) in the graph in "Allocations". If you do that, the object summary will show you the objects (most useful if you sort by "Live Bytes") that have been allocated and not released during that window of execution:
That can be helpful, but sometimes it's just cryptic CFString
or CGImage
allocations. It's therefore sometimes useful to see where in your code those objects were allocated. If you switch from "Statistics" - "Object Summary" to "Call tree", it will now show you how much memory was taken up by each of your methods (and I find this screen most useful if I also check "Invert Call Tree" and "Hide System Libraries"):
If you then double click on a symbol name here, it will actually show you the offending code:
Through that process, I can see what is being allocated at that spike, and I can now go about identifying why that memory is never getting released (in this case, it was my deliberate use of a repeating timer that I never invalidated
).
There are other tricks that are useful in more complicated scenarios (I'm particularly fond of having my code signaling flags that appear in instruments so I can more accurately correlate activities in my code with what's going on in Instruments), but that's probably too much to get into here. Hopefully this option-click-and-drag in Instruments will be a useful tool to identify what's being allocated and never released.