One problem with the error -[NSError dealloc] log log is that you still need to pass a pointer to NSError, otherwise there is no guarantee that the error will ever be generated. For example, it seems plausible that various framework methods can be implemented as follows:
if (outError) { *outError = [[[NSError alloc] init] autorelease];
You can make a global pointer, say:
NSError* gErrorIDontCareAbout = nil; NSError** const ignoredErrorPtr = &gErrorIDontCareAbout;
... and declare it as extern in the prefix header, and then pass ignoredErrorPtr any method whose error you do not want to represent, but then you lose some locale in terms of where the error occurred (and in fact it will work correctly, if you use ARC).
It occurs to me that what you really want to do is swizzle the designated initializer (or allocWithZone: and dealloc , and in this swizzled / wrapped method call [NSThread callStackSymbols] and attach the returned array (or maybe its -description ) to an NSError instance using objc_setAssociatedObject . Then in your swizzled -dealloc you can register the error itself and the call stack where it came from.
But no matter how you do it, I don’t think you can get anything useful if you just pass NULL , because frameworks may not create NSError in the first place, if you told them you were not interested in it (passing NULL ) .
You can do it as follows:
@implementation MyAppDelegate + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // Stash away the callstack IMP originalIMP = class_getMethodImplementation([NSError class], @selector(initWithDomain:code:userInfo:)); IMP newIMP = imp_implementationWithBlock(^id(id self, NSString* domain, NSInteger code, NSDictionary* dict){ self = originalIMP(self, @selector(initWithDomain:code:userInfo:), domain, code, dict); NSString* logString = [NSString stringWithFormat: @"%@ Call Stack: \n%@", self, [NSThread callStackSymbols]]; objc_setAssociatedObject(self, &onceToken, logString, OBJC_ASSOCIATION_RETAIN); return self; }); method_setImplementation(class_getInstanceMethod([NSError class], @selector(initWithDomain:code:userInfo:)), newIMP); // Then on dealloc... (Note: this assumes that NSError implements -dealloc. To be safer you would want to double check that.) SEL deallocSelector = NSSelectorFromString(@"dealloc"); // STFU ARC IMP originalDealloc = class_getMethodImplementation([NSError class], deallocSelector); IMP newDealloc = imp_implementationWithBlock(^void(id self){ NSString* logString = objc_getAssociatedObject(self, &onceToken); if (logString.length) NSLog(@"Logged error: %@", logString); originalDealloc(self, deallocSelector); // STFU ARC }); method_setImplementation(class_getInstanceMethod([NSError class], deallocSelector), newDealloc); }); } @end
Please note that this will log all errors, not just those that you do not handle. This may or may not be acceptable, but I'm struggling to come up with a way to make a distinction after the fact without any challenge in everything that you handle the error.