mahatmamanic

I'm a father, software guy, and student. I live in the Bay Area. My opinions are legion.

Memory Management in Objective C, Part 2

A couple days ago I talked a little bit about how the memory model works in Objective C. Today I want to expand on that a little bit, and talk about how to use the building blocks of the memory model to build your own objects that manage memory well. First I’ll talk about a few building blocks and code style issues, and then put together a full example of how this all works together.

Synthesized Properties

In case you’re unfamiliar with the way synthesized properties work, it’s worth taking a minute to talk about what they do behind the scenes. Imagine you have

NSString *words;
@property(nonatomic, retain) NSString *words;

and

@synthesize words;

Now when you assign to self.words there is some work that happens behind the scenes. In practice it’s roughly the equivalent of the following:

-(void) setWords:(NSString*)newWords
{
    if([newWords isEqual: words])
        return;

    [words release];
    words = newWords;
    [words retain];
}

There may actually be a bit more in there, but that’s the important bit. What’s most important here is that, when you combine this with Objective C’s ability to safely send a message to any nil object, you can free your existing memory in the property by calling self.words = nil;

Naming Ivars

Most examples you see will use the same ivar and property names. I think this is a bad idea for a couple reasons.

  1. If you have an ivar and a retain property both named words it’s very, very easy to forget to write self. when setting the property. If you forget self. then of course you set the ivar directly, which bypasses the retain/release mechanism baked into the synthesized property. A lot of PITA memory bugs trace back to this simple mistake.

  2. It’s lame to always have to write methods with parameters like aWords or newWords when what you really mean is words. But if your ivars and properties have the same names you’ll find yourself giving bad names to parameters to avoid stepping on your ivars with your locals

So what I like to do instead is to prepend my ivars with an identifying character. So instead of an ivar words I use _words. There is some small amount of contention that developers are not supposed to do this, because Apple reserves the right to do that themselves, but I’ve never had a problem with it. If you’re really worried, then use a different prepend, like iWords or mWords to identify ivars.

Here’s an example of how I prefer to name my ivars and map them to properties:

@interface SomeClass
{
    NSString *notLikeThis;
    NSString *_likeThis;
}

@property(nonatomic, retain) NSString *notLikeThis;
@property(nonatomic, retain) NSString *likeThis;

@end

@implementation SomeClass

@synthesize notLikeThis;
@synthesize likeThis = _likeThis;

@end

It’s a tiny bit of extra typing but believe me, it’s going to save you a lot of heartache.

Putting It Together

With synthesized properties and well named ivars, you can now easily put together classes that handle memory without you having to think much about it, as long as you follow a couple simple rules

  1. Always assign autoreleased objects to your retain properties: After your assignment, you want your property to hold the only count on the object. That way once the property releases the object its count will hit 0 and it will be deallocated. The best way to do this is to ensure that everything you assign to your retain properties is autoreleased.

  2. Never put an ivar on the left side of an assign: Assigning directly to the ivar doesn’t send a release to the existing object or retain the new one. Seeing an ivar on the left of an assign should be a giant red flag

  3. Always set your properties to nil in dealloc: By using the power of synthesized properties, setting the property to nil both sets the ivar to nil and releases whatever value is already there. If you’ve followed the other rules then this means the retain count should hit 0 and the object will be deallocated

If you follow those three rules you’ll find that memory management becomes easy (well there’s actually a fourth rule that I will cover below, but it will make more sense after an example). So without further ado, here’s an example class that manages memory in a reasonable way. This code does three things: compiles, doesn’t leak memory, and hopefully provides a useful illustration of the principles I’ve been discussing. Don’t expect it to do much more than that.

You’ll notice I don’t have calls to release all over the place, or any crazy memory drama. If you follow the simple set of rules, your memory management will just work.

The Fourth Rule

There’s one more rule that makes more sense once you’ve put everything together. You’ll notice that in my example above, delegate was an assign property rather than a retain. This is to avoid the trap of circular references leading to abandoned memory. Imagine a situation where an instance of MemoryExample is owned by some other object MemoryExampleDelegate further up the object graph. MemoryExampleDelegate has retained MemoryExample. If MemoryExample instances also retain their delegate, then there will be a circular retain as each instance retains the other. What you want is when you deallocate MemoryExampleDelegate that it nils all of its properties and they in turn are deallocated. But if the MemoryExample instance has retained MemoryExampleDelegate then that dealloc will never be called, and you’ll wind up with big chunks of objects that retain one another but are otherwise completely disconnected from the object graph. This is easily solvable by rule number four:

Retain Leaves, Assign Roots: If you imagine the object graph as a tree, then any node on the tree should be retaining its leaves, as it has an ownership stake in their existence. But it should only hold assign properties on any root nodes (delegates, parents, and the like) so that when their parents free them the dominos fall as intended, rather than leaving you with abandoned memory.

Aaaaand Scene

This is a bit of a ramble, but I hope that it’s helpful in explaining how memory works. If you have any questions let me know, and I’ll be happy to do a followup.

Done, and Gets Things Smart

I guess Done, and Gets Things Smart is a little bit old, but I hadn't seen it before today. It's long, but I think worth reading. It helped me to think some more about how to hire and build teams. Generally, when hiring, I think it's useful to ask myself after an interview "is this person better than me?" If the answer is No, then I should vote thumbs down. Mostly because the times I've been most happy at a job have been when I feel like the dumbest guy in the room.

Conversely, when interviewing for a gig it's probably a good idea to keep track of what percentage of the interviewers seemed flat-out better than you. If it's less than half of them then it might not be a great job to take. 

Memory Management in Objective C, Part 1

I interview a lot of people for jobs doing iPhone development, and it never ceases to amaze me how few people understand the memory management model in Objective C. I feel like there's something that can be a little better explained here, so I'm going to first do a post on the basics of memory management and then follow up with another post on how you can best organize your code to make things easy.

So first, the basics:

Memory Management in Objective C works on the principle of retain counts. An object exists as long as it has a retain count greater than 0. It is freed from memory whenever the count reaches 0. That's it. Nothing more complicated than that. 

Ok, then the next question should be "how is the retain count changed?" There are three primary ways to modify the retain count available to you, the programmer, and a few others that are handled by the system. First, the modifications within your control:

Basic Calls
  • retain: [object retain] you will add 1 to the count on object.
  • release: [object release] will subtract 1 from the count on object.
  • autorelease: [object autorelease] will subtract 1 from the count on object...just a little bit later, after the current run loop
All of that is pretty easy right? retain for +1, release for -1, autorelease for -1 at a slightly later date, and when we hit 0 get rid of the memory.

Ok, so what about creating objects? Well, there are a couple of simple rules in the standard SDK, and you should try to maintain those rules when you are writing code. 

Initializers
  • A method that includes the words init or copy is the same as _retain_, that is to say the object returned gets a +1 count
  • Any other initialization method is the same as retain plus autorelease, so it has a +1 NOW, and will get a -1 later, after the run loop

Collections
The final piece is to know what happens when an object is added to an array, dictionary, or other foundation collection class. Luckily, that's pretty simple. When you put an object into a collection, the collection gives the object +1. When it's removed from the collection, the object gets -1.

Sooo...
That's actually all there is to the basics. You want to make sure that the object keeps a count of 1 or more until you want to get rid of it, Then you want to have a count of 0 so that the object will be freed. When that happens its dealloc method is called, which will (hopefully) get rid of all of _its_ objects.

These rules may sound obscure, but I'll make another post soon explaining how you can leverage these rules to build your classes in a way that makes memory management easy.