Born Sleepy - Sam Deane's blog

  • about
  • code
  • music
  • contact
  • elegant chaos
Home › Blogs › Sam Deane's blog

Lazy Properties Revisited

Sam Deane — Wed, 2011-11-16 13:12

A while ago I blogged about how I’d really like Objective-C to have built in support for lazy properties.

My ideal solution would be something like this:

@property (nonatomic, retain, lazy) NSString* name;

The synthesized getter for this property would automatically call a method

 - (NSString*)nameInit 

when the property was first accessed, and would use the resultant value to initialise the property.

This has some advantages over any hand-rolled solution:

  • it can remain efficient
  • it can support the retain/copy semantics and atomicity of the property
  • it avoids the need for boiler-plate code for every property to test if it needs to be initialised

In the absence of this solution in the language, I offered a rather complicated set of macros to attempt to implement something similar.

Since then I’ve thought about it a bit more, and come up with another couple of ways of tackling the same task...

What Do We Want To Do?

Let’s say we’ve got a class:

@interface MyClass : NSObject

@property (nonatomic, retain) NSString* name;

@end

and we want to make the name property lazy.

What we need to do is to somehow insert some checking code into the -(NSString*)name getter function, that does something like this:

id value = self.name; // <-- this is a call to the ‘original’ getter
if (!value)
{
    value = [self nameInit];
    self.name = value;
}

return value;

Note that we need to be able to call the original getter here. We could attempt to access the underlying ivar directly, but using the original getter is much better as it preserves the property semantics regarding retain, copy etc.

Using Inheritance

One clean way to do this would be to insert a class in-front of our class, and implement our modified getter there. That allows us to cleanly call on to the original getter.

So we end up with something like this in our header file:

@interface MyClass : NSObject

@property (nonatomic, retain) NSString* name;

@end

@interface MyClassLazy : MyClass
@end

And this in our implementation file:

@implementation MyClass
@synthesize name;
// normal implementation here

// lazy initialisation function for name property
- (NSString*)nameInit
{
    return @"blah";
}
@end


@implementation MyClassLazy

- (NSString*)name
{
    id value = [super name];
    if (!value)
    {
        value = [self nameInit];
        self.name = value;
    }

    return value;
}

@end

Getting Things The Right Way Round

That's quite neat, but there's a problem. Our class is called MyClass, but in order to get the lazy behaviour, we need to use the class called MyClassLazy.

We can fix this with a bit of renaming. The header now looks like

@interface MyClassNonLazy : NSObject

@property (nonatomic, retain) NSString* name;

@end

@interface MyClass : MyClassNonLazy
@end

And the implementation:

@implementation MyClass
@synthesize name;
// normal implementation here

// lazy initialisation function for name property
- (NSString*)nameInit
{
    return @"blah";
}
@end


@implementation MyClass

- (NSString*)name
{
    id value = [super name];
    if (!value)
    {
        value = [self nameInit];
        self.name = value;
    }

    return value;
}

@end

Wrapping It Up Nicely

So that, in a nutshell, is a solution that works. With a bit of judicious macro definition, we can generalise this:

#define lazy_interface(class,parent) interface class##_nonlazy : parent
#define end_lazy_interface(class) end @interface class : class##_nonlazy @end

#define lazy_implementation(class) implementation class##_nonlazy
#define lazy_properties(class) end @implementation class
#define end_lazy_implementation(class) end

#define lazy_synthesize(prop) \
    class Dummy__; \
    - (NSString*)prop \
    { \
    id value = [super prop]; \
    if (!value) \
    { \
    value = [super prop##Init]; \
    self.test = value; \
    } \
    \
    return value; \
    }

So now, we can do this in the header:

@lazy_interface(TestClass, NSObject)

@property (nonatomic, retain) NSString* name;

@end_lazy_interface(TestClass)

And this in the implementation:

@lazy_implementation(TestClass)

@synthesize name;

// lazy initialisation function for name property
- (NSString*) nameInit
{
    return @"blah";
}

@lazy_properties(TestClass)

@lazy_synthesize(name)

@end_lazy_implementation(TestClass)

Can We Do Better?

Which is kind of nifty, but still, well, ugly. All our nasty macros make it not look like normal Objective-C.

So how about a different approach? Tune in to my next post...

Login

login or register

Navigation

  • Search
  • Recent posts

Recent Tweets

Recent Tunes

Recent Posts

  • Birthdays not showing in Lion iCal
  • Why Facebook Is Broken For Me
  • NSConference 2012
  • Radar? GTFO!
  • Is it just me?
  • Comments
  • What The Hell Happened To Xcode?
  • The Run Loop In Cocoa Unit Tests
  • Parameterised Unit Tests
  • Git Repository Migration
more

Links

  • Sam Deane's CV
  • Sam Deane on Facebook
  • Sam Deane on MySpace
  • Sam Deane In The Real World
  • Rangers Till I Die
  • UK Mac Developers Group
  • Mountain River Tai Chi Chuan
  • National Secular Society
  • Halflight Music
  • Alien Jones
  • Keith Deane Gardening

Syndicate content

  • about
  • code
  • music
  • contact
  • elegant chaos