Dynamic Message Binding in the Objective-C Runtime

Forgive the horrible title. I’ve just read one paragraph in the Apple document entitled “The Objective-C Programming Language” which explains how dynamic message binding, as implemented by the Objective-C runtime, is the seat of it’s power and versatility. Here’s that paragraph:

When a message is sent, a runtime messaging routine looks at the receiver and at the method named in the message. It locates the receiver’s implementation of a method matching the name, “calls” the method, and passes it a pointer to the receiver’s instance variables.

When you understand the consequences of this small paragraph and how it makes iOS messaging utterly different to static method binding, you’ll be well on your way to iOS enlightenment 😉

Advertisements

Objective-C class level “properties”. How to declare them and how to initialize them!

I wanted to have a private NSSet in my parent class to hold a collection of objects which could be searched by a class level method accepting a search item. I could easily have defined this NSSet as an instance @property and do lazy instantiation in its getter. But this would be inefficient as the set of objects would be identical in EVERY instance. But how to declare a class level “property” and initialize the members of my set?

Objective-C allows you to declare static variables at class level. Static variables only have one instance shared between all instances of your class. Private, static variables should be declared in your .m file within the @implementation – preferably just after @implementation for good code readability. The syntax is easy:

@implementation myClass

static NSSet *mySetOfObjects;

@end

But how do I initialize my NSSet? Here we use the magic initialize method:

+ (void)initialize {
    // Initialize your static variables here...
}

As you can see, initialize  is a class level method inherited from NSObject so therefore inherited by every class. The runtime sends initialize to each class in your program exactly one time – just before the class, or any class that inherits from it, is sent its first message from within your program. This is very efficient – kind of like lazy initialization for classes since if your class is never used initialize will never be invoked! Superclasses receive this message before their subclasses – this means there’s no need to invoke initialize on the superclass from within your code.

Putting this all together results in the following code, simple and efficient!:

@implementation MyClass

static NSSet *mySetOfObjects;

+ (void)initialize {
    mySetOfObjects = [[NSSet alloc] initWithObjects:@"one", @"two", @"three", nil];
}

+ (BOOL)isRecognizedString:(NSString *)searchItem {
    return [mySetOfObjects containsObject:searchItem];
}

@end

In this example I declare a class method which returns True if a string is contained within my set. The beauty is that every instance of this class will share the same code and the same static NSSet and the initialization of that set will happen only once and only if my class is actually used. Obviously this is just a simple example but these are the basics on which you can build.

Gotcha!

OK, when I said that initialize was called only once…it wasn’t the full truth. There is one caveat. Because of inheritance, an initialize message sent to a class that doesn’t implement the initialize method is forwarded to its superclass, even though the superclass will already have received the initialize message. For example, assume someone creates a class that inherits from our class but they don’t implement initialize in their class. When initialize is called on this subclass, because it isn’t handled, it will be called on its superclass – our class, instead. That means we need to insert a tiny bit of logic in the above code to guarantee that we only initialize our class once even in the above scenario. Don’t worry, just copy the method used in the code below.

@implementation MyClass

static NSSet *mySetOfObjects;

+ (void)initialize {
    if (self == [MyClass class]) {
        mySetOfObjects = [[NSSet alloc] initWithObjects:@"one", @"two", @"three", nil];
    }
}

+ (BOOL)isRecognizedString:(NSString *)searchItem {
    return [mySetOfObjects containsObject:searchItem];
}

@end

That if statement simply ensures that our initialization code only executes if ‘self’ is of class MyClass. If initialize is being called from a subclass then this will obviously be false, since the subclass must, by definition, be of a different type.

There, that final check will now ensure your code is rock solid 🙂