Recently, I came across a tricky problem in one of the projects that I am working on. We are using a framework which maps Objective-C classes to database columns. This framework will be accessing properties of the mapped class while saving and retrieving data. I have couple of classes, primary data-model class and a secondary data-model class which is used as property of the main class. The problem is to access all the properties of the secondary class through primary class.

Straightforward solution is to implement the getters and setters in the primary class to get and set the properties of the secondary class. This solution will solve the problem, but it is not very elegant and scalable. Looking for other solutions, I found the Objective-C concepts like Messaging and Message Forwarding which can be used for a scalable solution. Before providing the solution, I will explain in brief about the Messaging and Message Forwarding of Objective-C

Messaging

The Objective-C model of object-oriented programming is based on message passing to object instances. In Objective-C one does not simply call a method; one sends a message. This is unlike the Simula-style programming model used by C++. The difference between these two concepts is in how the code referenced by the method or message name is executed. In a Simula-style language, the method name is in most cases bound to a section of code in the target class by the compiler. In Smalltalk and Objective-C, the target of a message is resolved at runtime, with the receiving object itself interpreting the message.

The key to messaging lies in the structures that the compiler builds for each class and object. Every class structure includes these two essential elements:

  • A pointer to the superclass.
  • A class dispatch table. This table has entries that associate method selectors with the class-specific addresses of the methods they identify.

When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates the selector, the function calls the method entered in the table and passes it the receiving object’s data structure. This is the way that method implementations are chosen at runtime—or, in the jargon of object-oriented programming, that methods are dynamically bound to messages.

Message Forwarding

Sending a message to an object that does not handle that message is an error. However, before announcing the error, the runtime system gives the receiving object a second chance to handle the message.

You can implement a forwardInvocation: method to give a default response to the message, or to avoid the error in some other way. As its name implies, forwardInvocation: is commonly used to forward the message to another object.

Forwarding and Inheritance

Forwarding mimics inheritance, and can be used to lend some of the effects of multiple inheritance to Objective-C programs. An object that responds to a message by forwarding it appears to borrow or “inherit” a method implementation defined in another class. Forwarding not only mimics multiple inheritance, it also makes it possible to develop lightweight objects that represent or “cover” more substantial objects. Although forwarding mimics inheritance, the NSObject class never confuses the two. Methods like respondsToSelector: and isKindOfClass: look only at the inheritance hierarchy, never at the forwarding chain.

Surrogates

The surrogate stands in for the other object and funnels messages to it. If we use forwarding to set up a surrogate object or to extend the capabilities of a class, the forwarding mechanism should probably be as transparent as inheritance. If you want your objects to act as if they truly inherited the behavior of the objects they forward messages to, you’ll need to re-implement the respondsToSelector: and isKindOfClass: methods to include your forwarding algorithm. In addition the instancesRespondToSelector: method should also mirror the forwarding algorithm. If protocols are used, the conformsToProtocol: method should likewise be added to the list. Similarly, if an object forwards any remote messages it receives, it should have a version of methodSignatureForSelector: that can return accurate descriptions of the methods that ultimately respond to the forwarded messages.

As we have enough info about the Messaging and Message forwarding concepts, lets discuss the solution. In the project I am currently working on , there is a search feature. User can search for results using some criteria. This criteria consists of primary and advanced criteria, and datamodel classes are in place for both. Primary datamodel will contain secondary datamodel as a property. There is a framework which takes care of mapping database tables to specified datamodels. To save or retrieve data, the framework will be sending message to the mapped datamodel class. This framework doesn’t support nested mappings. Primary datamodel class is mapped to the database table, hence the framework will be expecting the columns to be mapped to the properties and will be sending setter and getter messages to the primary datamodel class. Solution is to make the advanced data model a surrogate of the primary data model. In the primary datamodel class, by overriding the following methods, the secondary datamodel is set up as the surrogate for the primary datamodel class.

 
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector 
{ 
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) { 
        signature = [surrogate instanceMethodSignatureForSelector:selector]; 
    } 
    return signature; 
} 

 
- (void)forwardInvocation:(NSInvocation *)anInvocation 
{ 
    if ([surrogate respondsToSelector:[anInvocation selector]]) 
        [anInvocation invokeWithTarget:surrogate]; 
    else 
        [super forwardInvocation:anInvocation]; 
} 

If I had used the initial approach of manually adding setters and getters, I would have ended up writing as many methods as twice the number of properties and had to make sure all the method signatures mimics the advanced criteria properties. Any changes to the name of the properties is a good entry point for regressin and code maintenance is going to be a mess. Other approach is to maintain a single datamodel which will represent all the criteria which will create other issues as the requirements for iPhone and iPad are different. Message forwarding approach reduced the amount of manual effort required and will be scalable even if there are changes to the datamodels.