December 18th, 2011 (trackback)
The retainCount method just isn’t very useful as has been indicated in the answers to about a zillion StackOverflow questions.
The documentation has this to say:
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
Makes it pretty clear that you shouldn’t be calling retainCount, but it really doesn’t illuminate exactly how useless the method is outside of a very narrow context.
So, let us count the ways:
- The absolute retain count of an object may change at any time once an object has been passed through any system API.
- Any subclass of any system provided class counts as “through system API”; the retain count may be impacted by implementation details.
- The retain count never reflects whether an object is autoreleased.
- Autorelease is a per-thread concept whereas retain counts are global; race condition derived hilarity can easily ensue.
- The
retainCount method can never return 0.
- Some classes are implemented as singletons some of the time.
- Some classes may internally manipulate their retain count directly (I.e. no swizzle for you!).
- While
retain/release are effectively thread safe, there is always a race between calling retainCount and having the actual retain count change in some other execution context.
Bottom line: the only time the absolute retainCount can be conclusively used for analytic purposes is if you have the backtrace of every retain and release that contributed to the current retain count’s value (there is another use case, documented at the end). If you have that, then you don’t need the retain count and, fortunately, Instruments is already generally quite adept at producing a per-object inventory of retains and releases for you (see Analyzing Data with the Allocations Instrument“.
In general, you should consider the retain count as a delta. Your code causes the retain count to increase and decrease. You don’t +alloc an object with a retain count of 1. Instead, you +alloc an object with a retain count of +1. If you want that object to go away, you need to do something — release, always and eventually — that causes the retain count to be decremented by 1.
It really is that simple.
Almost.
Concurrency — whether through threading or GCD — throws a bit of a wrench in the works (as it always does). Namely, the retain count should really be considered as a per concurrency context delta. If thread or queue A wants to do something with object X, it should hold a +1 retainCount on X for the duration of said operation. One subtle detail that will bear repeating later; an autorelease‘d object does not contribute to thread safety. That is, if thread or queue A wants to pass X to thread or queue B, there must be an explicit retain in A that is balanced by a release (or synchronization event back to A) in B.
Now, some of the enumerated claims may either seem specious at best or, certainly, not obvious as to exactly how it undermines the validity of the retain count. Read the rest of this entry »
Posted in Mac OS X, Objective-C, Software, Xcode |
16 Comments »