Starter Step

Recordable: Dynamic NSInvocation

Posted on: March 26, 2009

For UISpec I needed a an easy way to create and cache a method call on an object. I know that NSInvocation is the class to go with, but it’s very cumbersome to create and use. Luckily I found the blog post Callable Objects in Cocoa, which pointed me in the right direction, so I took what they suggested one step further and made it dynamic using message forwarding.

Say you have an address book object that models a list of addresses and you want to “record” a call that does a find by first and last name and “play” the call later.


//Create a Recordable instance with the addressBook as its target
Recordable *dvd = [Recordable withTarget:addressBook];

//Record the call you want to make
[dvd findAddressByFirstName:@"Joe" lastName:@"Smith"];

//Play it to get the results of the call
Address *address = dvd.play;

Pretty nifty huh? Here is the source code. Enjoy!

Recordable.h


@interface Recordable : NSObject {
	id target, play;
	NSInvocation *invocation;
}

@property(nonatomic, retain) id target;
@property(nonatomic, retain) NSInvocation *invocation;
@property(nonatomic, readonly) id play;

+(id)withTarget:(id)target;

@end

Recordable.m

<ol>
  <li>import "Recordable.h"</li>
</ol>

@implementation Recordable

@synthesize target, invocation;

+(id)withTarget:(id)target {
	return [[[self alloc] initWithTarget:target] autorelease];
}

-(id)initWithTarget:(id)_target {
	if (self = [super init]) {
		self.target = _target;
	}
	return self;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
	return [target methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation setTarget:target];
    [anInvocation retainArguments];
    [anInvocation setReturnValue:&self];
    self.invocation = anInvocation;
}

-(id)play {
	id value;
	[invocation invoke];
	NSString *returnType = [NSString stringWithFormat:@"%s", [[invocation methodSignature] methodReturnType]];
	if (![returnType isEqualToString:@"v"]) {
		id value;
		[invocation getReturnValue:&value];
		return value;
	} else {
		return nil;
	}
}

-(void)dealloc {
	self.target = nil;
	self.invocation = nil;
	[super dealloc];
}

@end

Advertisements
Tags: ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


  • Dave: I can tell you're a ruby guy because you forgot the 'return' keyword. Thanks for the tip though!
  • Chandrashekhar H M: Hi, Thanks its working fine in iOS 6 but not in iOS 7.0. Any Suggestion on this.
  • Coeur: To change a rootViewController, without all this TVNavigationController : myNewRoot = [[UIViewController alloc] init]; myNavigationController.view
%d bloggers like this: