In my previous post I presented a way to implement lazy properties.
It was a bit macro-heavy though, and looked kind of messy. I wondered if there was another way. Objective C is marvellously dynamic and introspective. Shouldn’t we be able to do something clever at runtime to achieve what we want?
In a word, the answer is yes, we can… Let’s say we adopt a standard pattern for the initialisation methods for any lazy properties, like we did in the previous implementation:
<prop>Init.
So for a property called name, the initialisation method would be called
nameInit
At class initialisation time, we can use Objective-C’s introspection routines to run through a list of all our class’s properties, looking for ones that have a lazy initialisation method.
For those that we find, we can swap out the normal implementation of the getter method for one which calls on to the original getter to get the current value, initialises it if the getter returned nil, then returns the value.
That sounds neat, but we will need to define a slightly different getter method for each property - since they all have to call on to a different original getter. Where are we going to put this code?
The answer turns out to be simple - we put it in the lazy initialisation method that we use to indicate that the property is lazy! So not only does the presence of the method tell us that it’s a lazy property, but the body of the method actually performs the lazy get.
Each of these new getter / lazy-initialisation methods is going to look pretty much the same - call the original getter, check the value, initialise if necessary, return the value. Most of the code is boilerplate, but the getter method that we need to call onto changes for each property.
We could probably do something clever here to work out the name of this property at runtime by performing some selector to string conversions, but we’d have to do it every single time the getter was called. As we don’t want the performance to totally suck, it would be good if we could just make a normal call to the original method, check the returned value, and only do potentially slow and clever things on the one occasion when we need to do some initialisation. We can achieve this quite easily with a macro that expands to a few lines of code for every lazy getter.
So what do we end up with?
The interface for our lazy class looks like this:
@interface TestClass : NSObject
@property (nonatomic, retain) NSString* test;
@end
Ok, that’s… erm… pretty normal!
The implementation looks like this:
@implementation TestClass
@synthesize test;
+ (void)initialize
{
if (self == [TestClass class])
{
[self initializeLazyProperties];
}
}
@lazy_synthesize(test, @"test value");
@end
That’s also pretty normal!
We only have two odd things going on here. First, we need to perform a one off initialisation when the class is first loaded, to hook up all the magic.
We can do this in the +initialize method of the class. We could make this step even briefer with a macro, but in this case I figured that it was cleaner to just put the code in.
The second thing we’re doing is “synthesizing” the lazy getter. We could easily just put in the actual boiler plate code each time, but since writing boiler-plate is really what we’re trying to get away from, a macro makes sense.
This solution is much cleaner, and it works. In my next post I’ll show you the implementation.