Paulo Poiati | Blog

@SuppressWarnings(“unused”)

  • Home
  • About
  • Github
  • GMongo
  • SandBox
Twitter Facebook RSS

Forwarding Objective-C Messages

Posted on 05/12/2009 by Paulo Poiati
2 commentsLeave a comment

Unlike Java or plain C, Objective-C work with messages. You don’t invoke a method on an object, instead, you send a message to it (like the Ruby language). This approach let a more dynamic behavior. For instance, suppose that you have this class:

// SomeClass
#import <Cocoa/Cocoa.h>
@interface SomeClass : NSObject {
}
-(void)doSomething
@end
#import "SomeClass.h"
#import "Delegate.h"
@implementation SomeClass
-(void)doSomething {
   NSLog(@"doSomething was called on %@", [self className]);
}
@end

As you can see it has only one instance method definition, called “doSomething”. Thus, it can respond to the “doSomething” message. But, what happens when we send a message that the receiver can’t respond to ? Lets make a try:

The receiver is the object that will receive the message. For example, in the following statement:

Dog *dog = [Dog new];
[dog bark];

dog is the receiver and bark is the message.

Here is our main code:

//MethodMissing
#import <Foundation/Foundation.h>
#import "SomeClass.h"
int main (int argc, const char * argv[]) {
  NSAutoreleasePool *pool = [NSAutoreleasePool new];
  SomeClass *someClass = [SomeClass new];
  [someClass doSomething];
  [someClass doSomethingElse];
  [pool drain];
  return 0;
}

This code compiles with warnings, because the message “doSomethingElse” is not defined in the SomeClass interface. Errors in the Objective-C message dispatch system occurs at runtime. Programmers need to be more careful when dealing with more dynamic languages.

Running the code we got the following result:


MethodMissing[1695:a0f] doSomething was called on SomeClass
MethodMissing[1695:a0f] -[SomeClass doSomethingElse]: unrecognized selector sent to instance 0x10010c6c0
MethodMissing[1695:a0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SomeClass doSomethingElse]: unrecognized selector sent to instance 0x10010c6c0'

Not surprisingly the program crashed. First it invoke our declared “doSomething” message and logged the message to the console, all ok until now. But, in line eight we got a NSInvalidArgumentException with the error “-[SomeClass doSomethingElse]: unrecognized selector sent to instance 0x10010c6c0″.

This error message is very readable, the problem is that, there isn’t any message that respond to “doSomethingElse” in the class “SomeClass”, even at runtime.

And we got to the key topic of this post. We can handle unrecognized selectors messages and do a special treatment when it arrive to the receiver. How we do that ?

We need to overwrite two methods from the NSObject class in the receiver class:
-(void)forwardInvocation:(NSInvocation *)invocation
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector

An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications.

Now our classes looks like this:

// ForwardClass
#import <Cocoa/Cocoa.h>
@interface ForwardClass : NSObject {
}
-(void)doSomethingElse;
@end
#import "ForwardClass.h"
@implementation ForwardClass
-(void)doSomethingElse {
	NSLog(@"doSomething was called on %@", [self className]);
}
@end
// SomeClass
#import <Cocoa/Cocoa.h>
@interface SomeClass : NSObject {
	id forwardClass;
}
-(void)doSomething;
@end
#import "SomeClass.h"
#import "ForwardClass.h"
@implementation SomeClass
-(id)init {
	if (self = [super init]) {
		forwardClass = [ForwardClass new];
	}
	return self;
}
-(void)doSomething {
	NSLog(@"doSomething was called on %@", [self className]);
}
-(void)forwardInvocation:(NSInvocation *)invocation {
	if (! forwardClass) {
		[self doesNotRecognizeSelector: [invocation selector]];
	}
	[invocation invokeWithTarget: forwardClass];
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
	NSMethodSignature *signature = [super methodSignatureForSelector:selector];
	if (! signature) {
		signature = [forwardClass methodSignatureForSelector:selector];
	}
	return signature;
}
@end

When an object receive a message it doesn’t recognize, it wraps the invocation in a NSInvocation object and call the -(void)forwardInvocation passing it as parameter. But, first, it call the -(NSMethodSignature*)methodSignatureForSelector to get the method signature for the given selector.

Now, running the same program again we got:


MethodMissing[523:a0f] doSomething was called on SomeClass
MethodMissing[523:a0f] doSomethingElse was called on ForwardClass

And the program finished without any problems.

There are a lot of uses for this technic, some examples are:

  • Wrap one object in a logger object that intercepts and records the invocation of interesting
    messages.

  • Implement “synthetic” messages that are handled by other methods in your class. Imagine creating a generic database record object that catches any property message it receives (i.e., -saleDate, -setSaleDate:) and automatically translates it into a record query. Instead of coding date = [record getDateFieldWithKey:@"SaleDate"], you could simply write date = [record saleDate], without ever writing a -saleDate method. NSManagedObject and CALayer are examples of classes that implement synthetic properties.
  • Create an object that forwards the message to a hierarchy of other objects, like a responder chain. Chapter 20 talks about responder chains. The proxy object would search a collection of other objects looking for one that implements the message

In a future post I will demonstrate one real world application of this.

Share and Enjoy

Categories: Objective-c | Tags: C, Objective-c
Notice: This work is licensed under a BY-NC-SA. Permalink: Forwarding Objective-C Messages
GMongo 0.5 Released

2 Responses to “Forwarding Objective-C Messages”

  1. Paulo Poiati says:
    10/02/2010 at 12:59

    Tarcio Santos :

    Iniciante em criar jogos para iphone

    Ola Paulo, primeiramente parabens pelo site e trabalhos relacionados.

    Sou iniciante e gostaria de algumas dicas antes de entrar de cabeca nesse novo universo da Mac OS(Jogos para iphone).

    Faco eng mecatronica na UPE, tenho conhecimento em linguagem C e so. Ainda nao tenho um Mac OS, tem como fazer jogos no windows? E quais programas eu preciso instalar pra comecar a desenvolver? (Um amigo me indicou o Eclipse e NetBeans)

    Pra resumir, o que eu preciso realmente saber para comecar a criar jogos para iphone?

    Agradeco a atencao e espero ansioso pela resposta, abraco.

    Tarcio Santos, Recife 12 de jan de 2010

    Desculpe a a demora Tarcio. Respondendo as suas perguntas:

    Sim, você precisa de um Mac p/ desenvolver p/ iPhone. Com um e mãos, você precisa baixar o IPhone SDK, disponível em http://developer.apple.com/iphone/. O SDK vem com uma IDE chamada: Xcode (consiste em um ambiente integrado p/ desenvolvimento de aplicativos p/ a Apple).

    Em relação a linguagem, recomendo você usar a linguagem nativa da Apple mesmo, o Objective-C, que é um superset da linguagem C que você já conhece.

    Falando em jogos, existe um framework bem legal que facilita e muito o desenvolvimento, me refiro ao: http://www.cocos2d-iphone.org/.

    Reply
  2. Tarcio Santos says:
    12/01/2010 at 19:10

    Iniciante em criar jogos para iphone

    Ola Paulo, primeiramente parabens pelo site e trabalhos relacionados.

    Sou iniciante e gostaria de algumas dicas antes de entrar de cabeca nesse novo universo da Mac OS(Jogos para iphone).

    Faco eng mecatronica na UPE, tenho conhecimento em linguagem C e so. Ainda nao tenho um Mac OS, tem como fazer jogos no windows? E quais programas eu preciso instalar pra comecar a desenvolver? (Um amigo me indicou o Eclipse e NetBeans)

    Pra resumir, o que eu preciso realmente saber para comecar a criar jogos para iphone?

    Agradeco a atencao e espero ansioso pela resposta, abraco.

    Tarcio Santos, Recife 12 de jan de 2010

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

*

*


question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

  • Recent Posts

    • Easyhash: An easy interface to generate md5 and sha1 hash in hexadecimal format
    • Grails / GORM: Changing default id name and type from an entity
    • Groovy / GMongo tips and tricks
    • GMongo available at Maven Central
    • GMongo 0.5 Released
  • Categories

    • GMongo
    • GORM
    • Grails
    • Groovy
    • Java
    • NoSQL
    • Objective-c
    • Programming
  • Tags

    C easyhash gmongo grails gorm groovy groovy java mongodb nosql Objective-c opensource
  • Blogroll

    • Rodrigo Lazoti Blog
  • Links

    • Mac Developers
  • Recent Comments

    • rohit on Groovy / GMongo tips and tricks
    • Paulo Poiati on GMongo 0.5 Released
    • Steve on GMongo 0.5 Released
    • Steve on GMongo 0.5 Released
    • Paulo Poiati on GMongo 0.5 Released
© Paulo Poiati | Blog. Proudly Powered by WordPress | Nest Theme by YChong