diff options
Diffstat (limited to 'Source/SPMainThreadTrampoline.m')
-rw-r--r-- | Source/SPMainThreadTrampoline.m | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/Source/SPMainThreadTrampoline.m b/Source/SPMainThreadTrampoline.m new file mode 100644 index 00000000..381e42ee --- /dev/null +++ b/Source/SPMainThreadTrampoline.m @@ -0,0 +1,153 @@ +// +// $Id$ +// +// SPMainThreadTrampoline.m +// sequel-pro +// +// Created by Rowan Beentje on 20/03/2010. +// Copyright 2010 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> +// + +#import "SPMainThreadTrampoline.h" + +@implementation NSObject (SPMainThreadTrampoline) + +/** + * Provide a category on all NSObjects to return a trampoline for that + * object on the main thread. + * This cannot be retained or released. + */ +- (id) onMainThread +{ + + // Return an autoreleased trampoline object + return [[[SPMainThreadTrampoline alloc] initWithObject:self] autorelease]; +} + +/** + * Provide a retained version of the category + */ +- (id) retainedOnMainThread +{ + return [[SPMainThreadTrampoline alloc] initWithObject:self]; +} + +@end + + +@implementation SPMainThreadTrampoline + +/** + * The master initiliasation - the category implementation calls this + * with the requested object. + */ +- (id) initWithObject:(id)theObject +{ + if (self = [super init]) { + trampolineObject = theObject; + } + return self; +} + +/** + * Delegate unrecognised methods to the trampolined objects, thanks to the magic + * of NSInvocation (see forwardInvocation: docs for background). Must be paired + * with methodSignationForSelector:. + */ +- (void) forwardInvocation:(NSInvocation *)theInvocation +{ + SEL theSelector = [theInvocation selector]; + if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + + // Retain the arguments and object for the call for safety + [theInvocation retainArguments]; + [trampolineObject retain]; + [theInvocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:trampolineObject waitUntilDone:YES]; + [trampolineObject release]; +} + +/** + * Return the correct method signatures for the trampolined object if + * NSObject doesn't implement the requested methods. + */ +- (NSMethodSignature *) methodSignatureForSelector:(SEL)theSelector +{ + NSMethodSignature *defaultSignature = [super methodSignatureForSelector:theSelector]; + if (defaultSignature) return defaultSignature; + + return [trampolineObject methodSignatureForSelector:theSelector]; +} + +/** + * Override the default repondsToSelector:, returning true if either NSObject + * or the trampolined object supports the selector. + */ +- (BOOL) respondsToSelector:(SEL)theSelector +{ + return ([super respondsToSelector:theSelector] || [trampolineObject respondsToSelector:theSelector]); +} + +/** + * Override the default performSelector:, again either using NSObject defaults + * or performing the selector on the trampolined object. + * Note that the return value from the trampolined object is not preserved in this case. + */ +- (id) performSelector:(SEL)theSelector +{ + if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector]; + + if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + + // Retain the object while performing calls on it + [trampolineObject retain]; + [trampolineObject performSelectorOnMainThread:theSelector withObject:nil waitUntilDone:YES]; + [trampolineObject release]; + + return nil; +} + +/** + * Override the default performSelector:withObject: - see performSelector: + * Note that the return value from the trampolined object is not preserved in this case. + */ +- (id) performSelector:(SEL)theSelector withObject:(id)theObject +{ + if ([super respondsToSelector:theSelector]) return [super performSelector:theSelector withObject:theObject]; + + if (![trampolineObject respondsToSelector:theSelector]) [self doesNotRecognizeSelector:theSelector]; + + // Retain the trampolined object, and the argument object, while performing calls + [trampolineObject retain]; + [theObject retain]; + [trampolineObject performSelectorOnMainThread:theSelector withObject:theObject waitUntilDone:YES]; + [theObject release]; + [trampolineObject release]; + + return nil; +} + +/** + * If the trampoline is sent the onMainThread category, just return the trampoline directly. + */ +- (id) onMainThread +{ + return self; +} + +@end
\ No newline at end of file |