-
-
Save yarshure/1599499 to your computer and use it in GitHub Desktop.
Spotify's invocation grabber, with some magic tricks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A | |
+(UIView*)flashAt:(CGRect)r in:(UIView*)parent color:(UIColor*)color; | |
{ | |
float duration = 0.5; | |
UIView *flash = [[[UIView alloc] initWithFrame:r] autorelease]; | |
flash.backgroundColor = color; | |
[parent addSubview:flash]; | |
[[flash invokeAfter:duration+0.1] removeFromSuperview]; | |
[UIView beginAnimations:@"SPFlash" context:NULL]; | |
[UIView setAnimationDuration:duration]; | |
flash.alpha = 0.0; | |
[UIView commitAnimations]; | |
return flash; | |
} | |
// B | |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
// Force the animation to happen by calling this method again after a small | |
// delay - see http://blog.instapaper.com/post/53568356 | |
[[self nextRunloop] delayedTableViewDidSelectRowAtIndexPath: indexPath]; | |
} | |
// C | |
[[tableView invokeAfter:0.15] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; | |
[[tableView invokeAfter:0.30] deselectRowAtIndexPath:indexPath animated:YES]; | |
[[tableView invokeAfter:0.45] selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@interface MyClass : NSObject | |
-(BOOL)areTheNewViewersGoneYet:(Duck*)duck; | |
@end | |
... | |
MyClass *myInstance = [[MyClass alloc] init]; | |
id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease]; | |
[invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9 | |
NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Cocoa/Cocoa.h> | |
#import "NSObject+SPInvocationGrabbing.h" | |
@interface Foo : NSObject { | |
int a; | |
} | |
-(void)startIt; | |
-(void)theBackgroundStuff; | |
-(void)theForegroundStuff; | |
@end | |
@implementation Foo | |
-(void)startIt; | |
{ | |
NSLog(@"Starting out on the main thread..."); | |
a = 3; | |
[[self inBackground] theBackgroundStuff]; | |
} | |
-(void)theBackgroundStuff; | |
{ | |
NSLog(@"Woah, this is a background thread!"); | |
a += 6; | |
[[self onMainAsync:YES] theForegroundStuff]; | |
} | |
-(void)theForegroundStuff; | |
{ | |
NSLog(@"Hey presto: %d", a); | |
} | |
@end | |
int main() { | |
NSAutoreleasePool *pool = [NSAutoreleasePool new]; | |
Foo *foo = [Foo new]; | |
[foo startIt]; | |
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; | |
[pool release]; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface SPInvocationGrabber : NSObject { | |
id _object; | |
NSInvocation *_invocation; | |
int frameCount; | |
char **frameStrings; | |
BOOL backgroundAfterForward; | |
BOOL onMainAfterForward; | |
BOOL waitUntilDone; | |
} | |
-(id)initWithObject:(id)obj; | |
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; | |
@property (readonly, retain, nonatomic) id object; | |
@property (readonly, retain, nonatomic) NSInvocation *invocation; | |
@property BOOL backgroundAfterForward; | |
@property BOOL onMainAfterForward; | |
@property BOOL waitUntilDone; | |
-(void)invoke; // will release object and invocation | |
-(void)printBacktrace; | |
-(void)saveBacktrace; | |
@end | |
@interface NSObject (SPInvocationGrabbing) | |
-(id)grab; | |
-(id)invokeAfter:(NSTimeInterval)delta; | |
-(id)nextRunloop; | |
-(id)inBackground; | |
-(id)onMainAsync:(BOOL)async; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "NSObject+SPInvocationGrabbing.h" | |
#import <execinfo.h> | |
#pragma mark Invocation grabbing | |
@interface SPInvocationGrabber () | |
@property (readwrite, retain, nonatomic) id object; | |
@property (readwrite, retain, nonatomic) NSInvocation *invocation; | |
@end | |
@implementation SPInvocationGrabber | |
- (id)initWithObject:(id)obj; | |
{ | |
return [self initWithObject:obj stacktraceSaving:YES]; | |
} | |
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; | |
{ | |
self.object = obj; | |
if(saveStack) | |
[self saveBacktrace]; | |
return self; | |
} | |
-(void)dealloc; | |
{ | |
free(frameStrings); | |
self.object = nil; | |
self.invocation = nil; | |
[super dealloc]; | |
} | |
@synthesize invocation = _invocation, object = _object; | |
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; | |
- (void)runInBackground; | |
{ | |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
@try { | |
[self invoke]; | |
} | |
@finally { | |
[pool drain]; | |
} | |
} | |
- (void)forwardInvocation:(NSInvocation *)anInvocation { | |
[anInvocation retainArguments]; | |
anInvocation.target = _object; | |
self.invocation = anInvocation; | |
if(backgroundAfterForward) | |
[NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; | |
else if(onMainAfterForward) | |
[self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; | |
} | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { | |
NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; | |
if (signature == NULL) | |
signature = [_object methodSignatureForSelector:inSelector]; | |
return signature; | |
} | |
- (void)invoke; | |
{ | |
@try { | |
[_invocation invoke]; | |
} | |
@catch (NSException * e) { | |
NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); | |
[self printBacktrace]; | |
printf("\n"); | |
[e raise]; | |
} | |
self.invocation = nil; | |
self.object = nil; | |
} | |
-(void)saveBacktrace; | |
{ | |
void *backtraceFrames[128]; | |
frameCount = backtrace(&backtraceFrames[0], 128); | |
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); | |
} | |
-(void)printBacktrace; | |
{ | |
for(int x = 3; x < frameCount; x++) { | |
if(frameStrings[x] == NULL) { break; } | |
printf("%s\n", frameStrings[x]); | |
} | |
} | |
@end | |
@implementation NSObject (SPInvocationGrabbing) | |
-(id)grab; | |
{ | |
return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; | |
} | |
-(id)invokeAfter:(NSTimeInterval)delta; | |
{ | |
id grabber = [self grab]; | |
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; | |
return grabber; | |
} | |
- (id)nextRunloop; | |
{ | |
return [self invokeAfter:0]; | |
} | |
-(id)inBackground; | |
{ | |
SPInvocationGrabber *grabber = [self grab]; | |
grabber.backgroundAfterForward = YES; | |
return grabber; | |
} | |
-(id)onMainAsync:(BOOL)async; | |
{ | |
SPInvocationGrabber *grabber = [self grab]; | |
grabber.onMainAfterForward = YES; | |
grabber.waitUntilDone = !async; | |
return grabber; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment