Lazy Properties In Objective-C

Lazy Properties

There was one other motivation for thinking about all of the Objective-C property stuff: Lazy Initialisation.

Lazy Initialisation is a GoodThingâ„¢, especially in the iOS world where memory is tight, and performance isn't always stellar.

So why the bloody hell didn't Apple add a lazy attribute to the property system, so that you could do this:

@property (retain, nonatomic, lazy) id foo;

In my world, that would synthesise a getter which performs a lazy initialisation the first time it's called, by calling a standard method (called fooLazyInit or something similar).

It seems to me that this would be a great boon to iOS developers. To some extent it also gets round the instance variable vs setter problem for the init method - the answer becomes simply not to initialise the property at that point.

All of this stuff has been mulling around in my brain for a while, so I was thinking, would it be possible to add some more property macros to my existing ones, like so:

ECPropertyDeclareLazy(name, type, attribute);
ECPropertySynthesizeLazy(name);

The answer is yes, it's a bit tricky, but it is possible, and here's how.

Implementing Lazy Properties

My main consideration was that I didn't want the macro to have to write all the proper getters and setters, since that would mean losing all of the retain/copy/nonatomic goodness that @property gives us (or recreating it in the macros).

So the trick is to wrap the generated getter with one that checks if the instance variable is nil. If it is, it initialises it directly by calling a special initialiser method. It then just calls on to the generated getter.

The solution I came up with looks like this to use:

@interface MyClass
{
    ECPropertyDefineVariable(foo, id);
}

ECPropertyDefineLazy(foo, id, retain, nonatomic);
@end

@implementation MyClass
ECPropertySynthesizeLazy(foo, setFoo, id);

- (id) fooInitLazy
{
    return [[[SomeClass alloc] init] autorelease];
}

@end

If you don't care about backwards compatibility with the old runtime, you can get rid of the ECPropertyDefineVariable bit.

The ECPropertyDefineLazy macro is defined like this:

#define ECPropertyDefineLazy(name, type, ...)       \
    @property (__VA_ARGS__) type name##Lazy; \
    @property (__VA_ARGS__) type name; \
    - (type) name##LazyInit

Here we actually declare two properties - one called foo, the other fooLazy. We're going to let Objective-C synthesise fooLazy for us, so we get the right retain behaviour etc.

We also forward declare our fooLazyInit method here. It's not strictly necessary, and we don't actually want anyone calling it, but it allows the actual implementation of the method to appear anywhere in the .m file, which is more convenient.

The ECPropertySynthesizeLazy macro is defined like this:

#define ECPropertySynthesizeLazy(name, setter, type)    \
    @synthesize name##Lazy = _##name; \
    - (type) name { if (!_##name) self.name##Lazy = [self name##LazyInit]; return   self.name##Lazy; } \
    - (void) setter: (type) value { self.name##Lazy = value; }

We let Objective-C synthesise the fooLazy method, but we ask it to use the foo instance variable. This isn't strictly necessary (it doesn't really matter what the variable is called), but it makes it consistent with the other property macros, and means that if you do try to access the instance variable directly it will be there and be called what you were expecting.

We then manually implement the getter and setter for the actual foo property.

The setter just calls on to the synthesised version. Annoyingly we have to pass in the name to use for this, since we can't automatically turn foo into setFoo as it involves turns "f" into "F" in a macro. We also have to pass in the type of the property to the synthesize macro, since it needs it to declare our getter and setter methods. In an ideal world it could work this out for itself.

The getter checks the instance variable directly to see if it's nil. If so, it calls fooLazyInit to get a value, and assigns it to the synthesised property. From then on, we just call on to that property to return a value.

To maintain the correct semantics for the property, we assign the result of the fooLazyInit method to the synthesised property using its setter. We could just set the instance variable directly, but that might not be atomic when it was supposed to be.

Assigning to the instance variable directly might also break the retain/assign/copy behaviour, so it's safer to go through the setter. To stick to the standard naming conventions, fooLazyInit should really autorelease any object that it allocates. This isn't ideal from an efficiency point of view, but it is cleaner. It allows us to call on to the synthesised setter and have it call retain if it's supposed to. Bear in mind that fooLazyInit is perfectly allowed to return an existing instance, so this isn't an irrelevance - we need to be certain that the retain/release semantics of fooLazyInit will always be consistent.

So What's The Point Of All This

Essentially, the point is that I've now got a relatively easy, and totally consistent, way of declaring properties to be lazy initialised.

There is a performance penalty, of course - since we have to check the instance variable.

If used wisely though, that's weighed against the performance gain achieved by not bothering to initialise properties until they're needed - which may be never.

It also means that you can clear properties more aggressively in response to low memory situations. You don't have to worry so much about the consequences when you know that the property will get created again on demand.

These macros are pretty new, and might turn out to be a complete cul-de-sac, but I'm going to give them a go.

If you want to try them out as well, I'll upload them to github: http://github.com/samdeane/code-snippets/tree/master/objective-c/properties/