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.