NSInvocation cleans code
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.
The code below has been updated accordingly.
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

3 Comments:
I &heart; trampolines.
By
Anonymous, At
12/20/2006 6:28 PM
This has really helped me out a lot -- but the one thing I've been struggling with is being able to do the equivalent of:
[NSObject cancelPreviousPerformRequestsWithTarget:self]
Is there any way to do this, so my view's deallocation can kill all remaining delayed performance requests?
By
jeffreality, At
2/07/2010 5:17 AM
The cancelPreviousPerformRequestsWithTarget message has to be send to the temporary _UFLatePerformer object. Therefore you have to have access to it.
So, taking this into account, I would modify the usage snippet to;
id lateP = [myObject performAfterDelay:0.0];
[lateP someMehodWithArgument1:argument1 argument2:argument2];
now you can cancel this request with:
[NSObject cancelPreviousPerformRequestsWithTarget:lateP];
I think this should work, but I have not tested it.
By
Tim Scheffler, At
2/09/2010 3:56 PM
Post a Comment
<< Home