Recently I was repairing someone's code. There was a big class that would not dealloc. You'd have to hit it with 5 or 6 releases to get it to dealloc.
I carefully looked through the big class and eventually found the various things that needed to be released.
This got me thinking: there just has to be some really easy way to "find" all the retains on an object .. am I right?
So, is there a simple way to "find all the retains" on an object? Is there a button in XCode or Instruments that everyone else knows about?
What do you do when you can't find a mystery retain like that?
So in the iOS universe, if anyone knows the "Show where all the retains came from on this object" button -- thanks!
P.S. Note that there is no leak, and this question is totally unrelated to leaks. The object simply "perfectly correctly" wouldn't release.
Later ..
Truly astounding solution by Fabio:
Fabio has provided an astounding solution to this problem. In nine words, here it is:
-(id)retain
{
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}
That is amazingly useful in many situations and leads to many other useful things. You've probably saved me two man-weeks of work per annum forever, Fabio. Thanks!
BTW if you're just getting to grips with this and struggling with the output, I saw that typically there will be many chunks featuring "UINib instantiateWithOwner:". It looks like those will come first, the significant chunks will follow.
Just guessing... but you may overwrite the retain method of the custom class calling super and throwing a nice NSLog to print the call stack.
Update with the actual code from Joe
Another important detail is that [NSThread callStackSymbols] returns a NSArray of NSStrings that can be filtered and used for other purposes. For example in complex and dynamic code, to check if a method properly causes another one to fire.
NOTE: In an ARC environment you will need to first add the
-fno-objc-arc
to compiler flags to allow you to override retain and call super.Instruments and its memory management stuff is your friend. Leaks and Zombies are two of the most valuable tools available. Use them.
Product -> Profile (or Cmd-I)
Instruments can show you the call stack for every malloc, release, and retain for any Obj-C object in your app with no code changes required. It works when you're using ARC, which is not the case for the solution from fabio.
It's really useful for finding those mystery retains - e.g. when an object just won't dealloc when it should.
Here's how:
Easy! (ish)
Place a breakpoint on custom class' retain
You could set a symbolic breakpoint on retain and then set it to the retain method of the custom class. The problem here is that retain is a method on
NSObject
so you will get the choice of all objective-c classes when placing the breakpoint.In this case it would be better to overwrite the retain method of the custom class with a call to super, so it would not do anything but you could then place a breakpoint in it.
Use a breakpoint action to log the caller
To add a breakpoint action double click on the blue marker. Find the breakpoint in the list and press the + button on the right. Then choose
Debugger command
and add the GDB commandframe 1
in this field, which will show you the caller of the retain. By this you cold log all retains and where they come from. When logging the releases in a similar way you could check what was the extra release.It is still a bit tedious, but this is the best I can think of.
Have you try using "Build & Analyse" in Xcode?
It's great for getting the bottom of objects not being released.
It is, unfortunately, not easily possible to programmatically determine what "owns" an object, since the idea of "object ownership" is a coding convention (unless you enable garbage collection).
Stack logging is often useful (I usually use a few breakpoints with
bt;continue
) but that only tells you the function that called retain, not the "bigger picture" (e.g. you might "transfer ownership" with[ivar2 release]; ivar2 = ivar1; ivar1 = nil;
). Sometimes it's a UIKit leak so you don't have the source code and you really have to go digging.If it's not a leak, however, call
-release
a few times and see where it crashes!