Update: (2008-08-07)
There's been a small (but potent) bug in the given implementation. I somehow move the release of the invocation:
[myInvocation release]; myInvocation = nil;
into the dealloc method of the _UFLatePerformer class. This is bollocks, because the invocation contains _UFLatePerformer as target itself. Therefore I have to release the invocation as soon as it has been invoked. Otherwise there is a retain-cycle and the dealloc-method of _UFLatePerformer is never called.
The code below has been updated accordingly.
During the development of Nifty Box I've grown to use NSInvocations more and more. One thing I am using this class especially for is a wrapper for NSObject's
performSelector:withObject:afterDelay:. This method invokes the selector with
one argument after a specified delay. My problem was that I can only pass one argument for the selector, but it often happens that I want to invoke a method with more than one argument. So I had to pack all the arguments into a NSDictionary, write a proxy method, inside this proxy function unpack the arguments from the dictionary and invoke the original method. This is messy.
For this I have written a small category add-on for NSObject:
@interface _UFLatePerformer : NSObject {
NSInvocation *myInvocation;
id target;
}
-(id)initWithTarget:(id)theTarget;
-(void)performLate;
@end
@interface NSObject (NSObject_laterInvocation)
-(id)performAfterDelay:(NSTimeInterval)delay;
@end
@implementation _UFLatePerformer
- (id) initWithTarget:(id)theTarget {
[super init];
if (self != nil) {
target = theTarget;
[target retain];
}
return self;
}
- (void) dealloc {
[target release]; target = nil;
[super dealloc];
}
-(void)forwardInvocation:(NSInvocation *)invocation;
{
myInvocation = invocation;
[myInvocation retain];
[myInvocation retainArguments];
}
-(BOOL)respondsToSelector:(SEL)aSelector;
{
BOOL result = [super respondsToSelector:aSelector];
if (result == NO)
result = [target respondsToSelector:aSelector];
return result;
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector;
{
NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
if (!result)
result = [target methodSignatureForSelector:aSelector];
return result;
}
-(void)performLate;
{
[myInvocation invokeWithTarget:target];
[myInvocation release]; myInvocation = nil;
}
@end
@implementation NSObject (NSObject_laterInvocation)
-(id)performAfterDelay:(NSTimeInterval)delay;
{
_UFLatePerformer *latePerformer = [[_UFLatePerformer alloc]
initWithTarget:self];
[latePerformer performSelector:@selector(performLate)
withObject:nil afterDelay:delay];
[latePerformer release];
return latePerformer;
}
@endThis basically works like the NSUndoManager's
[[undoManager prepareWithInvocationTarget:self] myMethod:someArgument anotherArgument:secondArgument]You use this new method like that:
[[myObject performAfterDelay:0.0] someMehodWithArgument1:argument1 argument2:argument2]
This is an implementation of the
Trampoline Object.
Feel free to use this snippet as you wish. No warranty however.
Labels: Cocoa, Objective-C, Programming