Skip to content

Instantly share code, notes, and snippets.

@nevyn
Created August 6, 2010 11:14

Revisions

  1. nevyn revised this gist Aug 28, 2010. 3 changed files with 86 additions and 6 deletions.
    16 changes: 12 additions & 4 deletions NSObject+SPInvocationGrabbing.h
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,21 @@
    #import <Foundation/Foundation.h>

    @interface SPInvocationGrabber : NSObject {
    id _object;
    NSInvocation *_invocation;
    int frameCount;
    char **frameStrings;
    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;
    @@ -19,4 +25,6 @@
    -(id)grab;
    -(id)invokeAfter:(NSTimeInterval)delta;
    -(id)nextRunloop;
    -(id)inBackground;
    -(id)onMainAsync:(BOOL)async;
    @end
    38 changes: 36 additions & 2 deletions NSObject+SPInvocationGrabbing.m
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@
    @interface SPInvocationGrabber ()
    @property (readwrite, retain, nonatomic) id object;
    @property (readwrite, retain, nonatomic) NSInvocation *invocation;

    @end

    @implementation SPInvocationGrabber
    @@ -31,10 +32,28 @@ -(void)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];
    @@ -45,7 +64,7 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
    }

    - (void)invoke;
    {
    {

    @try {
    [_invocation invoke];
    @@ -87,7 +106,22 @@ -(id)invokeAfter:(NSTimeInterval)delta;
    [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
    return grabber;
    }
    - (id)nextRunloop {
    - (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
    38 changes: 38 additions & 0 deletions main.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    #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;
    }
  2. nevyn revised this gist Aug 6, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile4.m
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ -(BOOL)areTheNewViewersGoneYet:(Duck*)duck;
    id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease];


    [invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]];
    [invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]]; // line 9


    NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation];
  3. nevyn revised this gist Aug 6, 2010. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions gistfile4.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    @interface MyClass : NSObject
    -(BOOL)areTheNewViewersGoneYet:(Duck*)duck;
    @end
    ...
    MyClass *myInstance = [[MyClass alloc] init];
    id invocationGrabber = [[[SPInvocationGrabber alloc] initWithTarget:myInstance] autorelease];


    [invocationGrabber areTheNewViewersGoneYet:[Duck yellowDuck]];


    NSInvocation *invocationForAreTheNewViewersGoneYet = [invocationGrabber invocation];
  4. nevyn revised this gist Aug 6, 2010. 1 changed file with 28 additions and 0 deletions.
    28 changes: 28 additions & 0 deletions gistfile3.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    // 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];
  5. nevyn revised this gist Aug 6, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion NSObject+SPInvocationGrabbing.m
    Original file line number Diff line number Diff line change
    @@ -83,7 +83,7 @@ -(id)grab;
    }
    -(id)invokeAfter:(NSTimeInterval)delta;
    {
    id grabber = [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
    id grabber = [self grab];
    [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
    return grabber;
    }
  6. nevyn revised this gist Aug 6, 2010. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions NSObject+SPInvocationGrabbing.m
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    #import "NSObject+Spotify.h"
    #import "NSObject+SPInvocationGrabbing.h"
    #import <execinfo.h>

    #pragma mark Invocation grabbing
    @@ -51,7 +51,7 @@ - (void)invoke;
    [_invocation invoke];
    }
    @catch (NSException * e) {
    SPNSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
    NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
    [self printBacktrace];
    printf("\n");
    [e raise];
  7. nevyn created this gist Aug 6, 2010.
    22 changes: 22 additions & 0 deletions NSObject+SPInvocationGrabbing.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    #import <Foundation/Foundation.h>

    @interface SPInvocationGrabber : NSObject {
    id _object;
    NSInvocation *_invocation;
    int frameCount;
    char **frameStrings;
    }
    -(id)initWithObject:(id)obj;
    -(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
    @property (readonly, retain, nonatomic) id object;
    @property (readonly, retain, nonatomic) NSInvocation *invocation;
    -(void)invoke; // will release object and invocation
    -(void)printBacktrace;
    -(void)saveBacktrace;
    @end

    @interface NSObject (SPInvocationGrabbing)
    -(id)grab;
    -(id)invokeAfter:(NSTimeInterval)delta;
    -(id)nextRunloop;
    @end
    93 changes: 93 additions & 0 deletions NSObject+SPInvocationGrabbing.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    #import "NSObject+Spotify.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;

    - (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation retainArguments];
    anInvocation.target = _object;
    self.invocation = anInvocation;
    }
    - (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) {
    SPNSLog(@"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 = [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
    [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
    return grabber;
    }
    - (id)nextRunloop {
    return [self invokeAfter:0];
    }
    @end