NSObject+SPInvocationGrabbing.m 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #import "NSObject+SPInvocationGrabbing.h"
  2. #import <execinfo.h>
  3. #pragma mark Invocation grabbing
  4. @interface SPInvocationGrabber ()
  5. @property (readwrite, retain, nonatomic) id object;
  6. @property (readwrite, retain, nonatomic) NSInvocation *invocation;
  7. @end
  8. @implementation SPInvocationGrabber
  9. - (id)initWithObject:(id)obj;
  10. {
  11. return [self initWithObject:obj stacktraceSaving:YES];
  12. }
  13. -(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
  14. {
  15. self.object = obj;
  16. if(saveStack)
  17. [self saveBacktrace];
  18. return self;
  19. }
  20. -(void)dealloc;
  21. {
  22. free(frameStrings);
  23. self.object = nil;
  24. self.invocation = nil;
  25. [super dealloc];
  26. }
  27. @synthesize invocation = _invocation, object = _object;
  28. @synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
  29. - (void)runInBackground;
  30. {
  31. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  32. @try {
  33. [self invoke];
  34. }
  35. @finally {
  36. [pool drain];
  37. }
  38. }
  39. - (void)forwardInvocation:(NSInvocation *)anInvocation {
  40. [anInvocation retainArguments];
  41. anInvocation.target = _object;
  42. self.invocation = anInvocation;
  43. if(backgroundAfterForward)
  44. [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil];
  45. else if(onMainAfterForward)
  46. [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone];
  47. }
  48. - (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector {
  49. NSMethodSignature *signature = [super methodSignatureForSelector:inSelector];
  50. if (signature == NULL)
  51. signature = [_object methodSignatureForSelector:inSelector];
  52. return signature;
  53. }
  54. - (void)invoke;
  55. {
  56. @try {
  57. [_invocation invoke];
  58. }
  59. @catch (NSException * e) {
  60. NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e);
  61. [self printBacktrace];
  62. printf("\n");
  63. [e raise];
  64. }
  65. self.invocation = nil;
  66. self.object = nil;
  67. }
  68. -(void)saveBacktrace;
  69. {
  70. void *backtraceFrames[128];
  71. frameCount = backtrace(&backtraceFrames[0], 128);
  72. frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
  73. }
  74. -(void)printBacktrace;
  75. {
  76. for(int x = 3; x < frameCount; x++) {
  77. if(frameStrings[x] == NULL) { break; }
  78. printf("%s\n", frameStrings[x]);
  79. }
  80. }
  81. @end
  82. @implementation NSObject (SPInvocationGrabbing)
  83. -(id)grab;
  84. {
  85. return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease];
  86. }
  87. -(id)invokeAfter:(NSTimeInterval)delta;
  88. {
  89. id grabber = [self grab];
  90. [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
  91. return grabber;
  92. }
  93. - (id)nextRunloop;
  94. {
  95. return [self invokeAfter:0];
  96. }
  97. -(id)inBackground;
  98. {
  99. SPInvocationGrabber *grabber = [self grab];
  100. grabber.backgroundAfterForward = YES;
  101. return grabber;
  102. }
  103. -(id)onMainAsync:(BOOL)async;
  104. {
  105. SPInvocationGrabber *grabber = [self grab];
  106. grabber.onMainAfterForward = YES;
  107. grabber.waitUntilDone = !async;
  108. return grabber;
  109. }
  110. @end