Memory Graph Debugger Tips
I’ve been using Xcode’s new memory graph debugger for just about a day, so I don’t have a ton to share here, but I do have a few things.
To start, hit the rotated Sleestak-fingers button in the debugger. It’s between the Cyberman button (view debugger) and paper airplane (location simulator). In other words: it’s not in Instruments. It’s in Xcode.
Turn off zombies. If your scheme has zombies on, you’re going to get a bunch of extra noise. (Tip: keep zombies off in general until you need them.)
Turn on Malloc Stack Logging in Diagnostics in your scheme. (I think I have it right that this needs to be on in order for the memory graph debugger to show backtraces.)
Open the right-hand sidebar in Xcode. Clicking on an object shows its class, hierarchy, and backtrace.
Lines between objects have a label. A line represents a reference. Click on the label to see if the reference is strong or weak or unknown and what the source and destination are.
Don’t click on anything where the name looks something like
MagicOb(something like that). It crashes Xcode for me every time.Click on the circle-with-double-arrows to expand a tree. To unexpand, click again in that same spot. (The arrows point inward now instead of outward.) However, there’s a bug where sometimes this disappears. Select something else in the left-hand sidebar and then come back, and the collapse arrows should appear.
In the left-hand sidebar, look for the purple icon with the ! inside. These indicate possible problems.
However, most problems aren’t detected. It’s up to you go through and see what’s hanging around that should not be.
I’ve fixed two bugs using the memory graph debugger, and I saved a bunch of time in both occasions. It’s probably worth telling about them as a reminder of the kinds of problems you can run into.
Notification block
An NSNotification observer was set up using a block — which is something I myself don’t do, since it litters an init or viewDidLoad with extraneous code and since it’s dangerous.
It’s dangerous because, unless you remember to be careful, it can capture a strong reference to self, and then that object is never going to go away. I don’t like APIs that require the developer to remember extra things like this.
And, sure enough, this was one of those cases.
The tipoff was in the memory graph debugger: the reference was labelled as “capture,” which let me know there was a block doing a capture, and it was then pretty quick to find out where.
(See also, from 2015: How Not to Crash #3: NSNotification.)
View controller / view retain cycle
There’s a general rule of programming that says objects should know about their children but not about their parents.
However, sometimes a view needs to know about its view controller. This is less than ideal, but sometimes it’s the least-bad option. (Well… I’m skeptical — but it happens, and we ship great apps, so there ya go.)
The related rule of programming says that if a child knows about its parent, it still can’t hold a strong reference to its parent.
That’s what was happening here: a view was retaining its view controller. The simple fix was to make that a weak property.
And, again, the memory graph debugger took me right to this. I could see what was happening inside the app in a way I never could before.
It’s marvelous. You should use it.