Skip to content

Instantly share code, notes, and snippets.

@epatey
Last active August 29, 2015 14:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save epatey/02d8ca2b1e8593a6ce7a to your computer and use it in GitHub Desktop.
Save epatey/02d8ca2b1e8593a6ce7a to your computer and use it in GitHub Desktop.
NSObject category that explodes if an object doesn't get deallocated soon
// Use this gist when you need to assert that a particular object is deallocated
// "soon". I use this technique extensively to ensure that I don't introduce retain
// cycles into my code. It really beats not noticing that you regressed and started
// leaking a view controller months ago!
@interface NSObject (ExplodingDealloc)
// Explode if this object isn't deallocated within 3 seconds
- (void)explodeIfNoDealloc;
// Return a signal that will explode if the object isn't deallocated within
// 3 seconds of subscription.
- (RACSignal *)explodingDeallocSignalWithTimeout:(NSTimeInterval)expiration;
@end
@implementation NSObject (ExplodingDealloc)
- (void)explodeIfNoDealloc {
[[self explodingDeallocSignalWithTimeout:3] subscribeCompleted:^{}];
}
- (RACSignal *)explodingDeallocSignalWithTimeout:(NSTimeInterval)expiration {
NSString *description = self.description;
return [[[self.rac_willDeallocSignal
timeout:expiration onScheduler:[RACScheduler mainThreadScheduler]]
doCompleted:^{
NSLog(@"explodingDeallocSignal - %@ did dealloc", description);
}]
doError:^(NSError *error) {
NSAssert(NO, @"explodingDeallocSignal - %@ not deallocated", description);
}];
}
// If you're not using ReactiveCocoa, you're living half a life. Nevertheless,
// here's a version for you.
- (void)nonRACVersion {
NSString *description = self.description;
__weak NSObject *weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSObject *strongSelf = weakSelf;
if (strongSelf) {
NSAssert(NO, @"explodeIfNoDealloc - %@ not deallocated", description);
}
else {
NSLog(@"explodeIfNoDealloc - %@ did dealloc", description);
}
});
}
@end
@epatey
Copy link
Author

epatey commented Nov 10, 2014

For example, when dismissing a view controller, I often have code like the following:

[self presentViewController:childController
                   animated:YES
                 completion:nil];
...
[self dismissViewControllerAnimated:YES
                         completion:nil];
[childController explodeIfNoDealloc];
childController = nil;

Also, if a view controller owns the lifetime of its view model, I'll do the following in the view controller's dealloc

- (void)dealloc {
  [self.viewModel explodeIfNoDealloc];
}

@leoschweizer
Copy link

Nice idea. However, both versions always explode even on the simplest [[NSObject new] explode] (wrapped in a method call, which means that the new object should be deallocated). In all places I tried it, the use of explode actually prevented deallocation, even on objects which where deallocated without it. However, I don't have an explanation for that yet - unfortunately.

A second remark: I would strongly suggest to wrap the invocation in something like

#ifdef DEBUG
    #define DeallocGuard(obj) ([obj explode])
#else
    #define DeallocGuard(obj)
#endif

- (void) dealloc {
    DeallocGuard(self.viewModel);
}

saves you a lot of boilerplate code you don't really want to have in your archives - or do you ;) ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment