Memory management guidelines

In this post, I’ll outline four simple rules for memory management in Objective-C. The Apple docs have already established basic guidelines for object ownership, following the fundamental idea that if you own an object, then you’re responsible for releasing it. This post goes a step further with tips for when and where ownership can be obtained or released in your code.

The Traditional Golden Rule

If a method name contains ‘alloc,’ ‘copy,’ or ‘new,’ then you own the returned object. Otherwise any new objects received through a method call are autoreleased.

This is really just a naming convention that makes it clear who is responsible for releasing objects. It doesn’t make any specifications about where it’s appropriate to obtain or release ownership in your code.

The Four Rules

The rules dictate where any object allocation and releasing may take place. Outside of these conditions, all objects must not change ownership status, and any new objects received through method calls must be implicitly autoreleased. In other words, these are the only places where it’s acceptable to call any alloc, new, copy, release, or autorelease method:

1. In init (obtain) and dealloc (release) methods.
2. In setter methods (both obtain & release).
3. Working with static variables (both obtain & release).
4. (optional) In autonomous constructors (obtain) and completion callbacks (release).

These rules, explained in more detail below, can easily be followed with the help of the be method.

Rule 1: Init / Dealloc

Objects can be obtained in any init method, with each owned object having a corresponding release or nil-set property value in dealloc. It is recommended to avoid using property setters in init since setters may depend on your object being in a completely initialized state. Convenience constructors (e.g. stringWithFormat: ) can rely on init methods and property setters to obtain all necessary objects.

Rule 2: Setters

We can divide all object instance variables into three classes: weak pointers (objects we don’t own, such as a standard delegate pointer), strong lifetime pointers which point to the same owned object for the lifetime of the owner, and strong moving pointers which may point to different objects within the owner’s lifetime. Note that the content of strong lifetime pointer objects can still change over time, such as an NSMutableArray that grows/shrinks; what can’t change is the pointer itself.

In the case of weak pointers, there is no memory management to worry about. For strong lifetime pointers, things can be handled completely in init/dealloc. These rules suggest that, for all strong moving pointers, you use a property to handle all assignments. This way, all releasing and obtaining is nicely encapsulated in the setter method.

So if an instance variable is a strong pointer that can be assigned to outside of init, make it a property, and only assign to this pointer through the property’s setter (except in init, as mentioned above). In the dealloc method, it’s safer to release by setting self.myVar = nil than [myVar release] because the release can leave around an invalid pointer.

This rule and the last cover the majority of all memory management in typical Objective-C code. The next two rules focus on relatively special cases to make these rules more comprehensive.

Rule 3: Static Variables

Static variables can be used for singletons or other shared resources.  For this rule, global variables are also considered static, although conventional wisdom tells us it’s better to use static variables within a method (or perhaps at the file level) than a true global.  You can obtain or release a static variable anywhere in code, although it is recommended that all memory management for a single static variable be as localized as possible.

Rule 4: Autonomous Objects

Autonomous objects are objects not owned by anyone in the creator’s ownership tree.  One example is an NSTimer created by a call to one of the scheduleTimerWithTimeInterval: methods, where the returned value is ignored.  The created object is retained by someone else (NSTimer is retained by the run loop in the example), and continues to be useful via its callbacks (such as the timer firing).  A complete discussion of autonomous objects is worth its own blog post.

A constructor method that creates an autonomous object is allowed to obtain that object, and completion callbacks are allowed to release it.  For example, if you wrote your own timer, you could create a factory method that returned a retained timer, and the callback for when the timer was fired could release it.

This last rule is sketchier than the first three, so I’m marking it as optional.  Here are the objections: First, it’s unusual to have a retained object outside your code’s ownership.  There are some Apple methods that allow this behavior, such as NSTimer or UIAlertView, but they don’t really require it, either.  Second, it goes against the grain to have one object retain something and another release it.  You could think of this pattern as passing ownership to the caller, but it is still unorthodox.  I’m including it because I find it incredibly useful, but I don’t expect everyone to agree on this rule.

Why the Rules Work

The motivation behind the rules is to keep your pre-debug memory management code leak-free by enforcing a system that makes it harder to mess things up.  The main idea is that the more localized and patterned your obtain/release pairs are, the easier they are to get right.  So the rules aim to minimize where you can make these calls, and keep the pairing consistent.

At the same time, they are designed to never stop you from doing what you want with your objects. Variables in your methods are temporary, and can use the be method to create new temporary objects for the lifetime of the method.  Instance variables can do whatever they want within these rules, as explained in the categorization explanation for setters.  And static/autonomous objects get special treatment.

Having personally moved from a C++ environment where I enjoyed the security of self-deleting smart pointers (which can’t exist in Objective-C), I find that these rules go a long way in keeping my code clean, and I hope they may be helpful for others as well.

One Comment