aboutsummaryrefslogtreecommitdiff
path: root/VKPC/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
blob: c48ea0894b29f779ba230468a13de7ca70e445d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#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 {
    @try {
        [self invoke];
    }
    @finally {}
}


- (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];
}

- (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