Debug reference counting for any class

In a previous post, I mentioned a trick that I sometimes use to debug memory issues where a class I own is either being released too soon (often resulting in a EXC_BAD_ACCESS exception), or is leaked, resulting in low memory. The main idea was to temporarily override key methods such as retain, release, and autorelease on the class, and use a breakpoint or log statements to gain more information about where and when the reference count is going wrong.

The trouble with that technique is that you can only override retain and release on classes you own. What if you’re working with a class whose implementation is not available to you? This happens often – for example, actual objects of type UIButton, NSString, or NSArray are most often (if not always) going to be a subclass of those classes which it would be bad practice to try to predict at compile-time. Despite the inaccessibility of these classes, you can still do something very useful, although it is technically a little dangerous.

Override retain and release on any class

The trick is to simply use a category to override the desired methods. If you have a handy PrintStackTrace function from last week’s post, then you can get log data on every memory management call on your object like this:

void PrintTraceForObject(id obj) {
  NSLog(@"object: %p", obj);
  PrintStackTrace();
}

@interface UIButton (MemDebug)

+ (id)alloc;
- (id)retain;
- (id)autorelease;
- (void)release;

@end

@implementation UIButton (MemDebug)

+ (id)alloc {
  id obj = [super alloc];
  PrintTraceForObject(obj);
  return obj;
}

- (id)retain {
  PrintTraceForObject(self);
  return [super retain];
}

- (id)autorelease {
  PrintTraceForObject(self);
  return [super autorelease];
}

- (void)release {
  PrintTraceForObject(self);
  [super release];
}

@end

This will give you a detailed log of exactly when and where all reference-count changes (retains or releases) were made. If anything is going wrong, this should be very illuminating.

Why it’s dangerous

It’s not a perfect solution. It works because it’s extremely unusual for anyone to declare a category like this.

In more detail: If you declare a category with methods that are already defined, then those methods become hijacked by the category versions. It’s as if the original definitions for that class didn’t exist, and only the category versions did exist.

So what happens if you define two categories with the same methods on the same class? You can legally do that. However, that would be a horrible nefarious thing to do, because the compiler makes no guarantees about which methods end up actually taking precedence. So if you did that, you’d have no idea which actual implementation would be ultimately used. Kind of scary, right? That’s why I’m calling this technique dangerous.

By the way, if you ever do want to add your own functionality to a particular pre-existing method – without destroying the existing method – then you can use a trick called method swizzling. But that’s a subject for another post.

For now, enjoy this extremely useful memory debugging trick!

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*