// // HOMTrampoline.m // HigherOrderMessaging // // Created by Ofri Wolfus on 09/09/05. // Copyright 2005 Ofri Wolfus. All rights reserved. // #import "HOMTrampoline.h" #import "NSObject-HOMPrivateAdditions.h" #import "NSInvocationAdditions.h" #import "HOMUtilities.h" @implementation HOMTrampoline //Returns a new trampoline + (id)trampolineWithTarget:(id)aTarget selector:(SEL)aSelector { return [[self alloc] initWithTarget:aTarget selector:aSelector]; } //Create a new trampoline with the passed lifetime + (id)trampolineWithTarget:(id)aTarget selector:(SEL)aSelector lifeTime:(unsigned int)numberOfInvocations { return [[self alloc] initWithTarget:aTarget selector:aSelector lifeTime:numberOfInvocations]; } //Init - (id)initWithTarget:(id)aTarget selector:(SEL)aSelector { return [self initWithTarget:aTarget selector:aSelector lifeTime:1]; } //Init - (id)initWithTarget:(id)aTarget selector:(SEL)aSelector lifeTime:(unsigned int)numberOfInvocations { selector = aSelector; lifeTime = numberOfInvocations; invocationsCount = 0; target = nil; [self setTarget:aTarget]; if (lifeTime > 1) invocations = [[NSMutableArray alloc] initWithCapacity:lifeTime]; return self; } //Clean up - (void) dealloc { [invocations release]; invocations = nil; [target release]; target = nil; selector = NULL; [super dealloc]; } /*- (NSString *)description { return [NSString stringWithFormat:@"HOMTrampoline <%p>: {target = %@, selector = %@}", (void *)self, target, NSStringFromSelector(selector)]; }*/ //As a trampoline, we respond to anything - (BOOL)respondsToSelector:(SEL)aSelector { return YES; } //Forward any message back to our target - (void)forwardInvocation:(NSInvocation *)anInvocation { [anInvocation setTarget:target]; if (invocations) { ++invocationsCount; [invocations addObject:anInvocation]; if (invocationsCount < lifeTime) { [anInvocation setNonRetaindReturnValue:self]; //Return ourself } else { //Forward the message [target performSelector:selector withObject:invocations]; //Release ourself [self release]; } } else { //Forward the message [target performSelector:selector withObject:anInvocation]; //Release ourself [self release]; } } //We give a chance for our target to return us any method signature it thinks is right by implementing -HOMMethodSignatureForSelector:. //If our target can not respond to -HOMMethodSignatureForSelector: then we'll try to get a signature by calling //-methodSignatureForSelector:. If that fails as well, we'll return a signature which returns an id and takes variable arguments of //type id (variable arguments are not supported by NSMethodSignature in 10.4, but if they'll be someday, we're ready for it). - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *result = nil; if ([target respondsToSelector:@selector(HOMMethodSignatureForSelector:)]) result = [target HOMMethodSignatureForSelector:aSelector]; //If our target is an instance, we send it a -methodSignatureForSelector: message. //If not, it's probably a class, and therefor we first try -methodSignatureForSelector:, and if that fails, -instanceMethodSignatureForSelector:. if (!result) result = (HOMObjectIsInstance(target) ? [target methodSignatureForSelector:aSelector] : ([target methodSignatureForSelector:aSelector] ? : [target instanceMethodSignatureForSelector:aSelector])); //Make sure we return a signature. //Instead of hacking our way to create an NSMethodSignature instance, we simply use the signature //of NSArray's -initWithObjects: which is exactly what we need. if (!result) result = [NSArray instanceMethodSignatureForSelector:@selector(initWithObjects:)]; return result; } //===================================================================== //============================= Accessors ============================= //===================================================================== #pragma mark Accessors //Set our target - (void)setTarget:(id)newTarget { if (target != newTarget) { [target release]; target = [newTarget retain]; } } //Return our target - (id)target { return target; } //Set our selector - (void)setSelector:(SEL)newSelector { selector = newSelector; } //Return our selector - (SEL)selector { return selector; } @end