Skip to content

Instantly share code, notes, and snippets.

@cconstable
Forked from anonymous/gist:a349ad59282d9770bb64
Last active August 29, 2015 14:08
Show Gist options
  • Save cconstable/c1807469bebf74c08d01 to your computer and use it in GitHub Desktop.
Save cconstable/c1807469bebf74c08d01 to your computer and use it in GitHub Desktop.
#import "AppDelegate.h"
#import <objc/runtime.h>
static NSString *Key = @"Key";
@interface Source : NSObject
@end
@implementation Source
- (void)dealloc
{
NSLog(@"Dealloc Source");
}
@end
@interface Target : NSObject
@property (assign, nonatomic) Source *unsafeSource;
@end
@implementation Target
- (void)dealloc
{
NSLog(@"Dealloc Target");
}
@end
@interface Watcher: NSObject
@property (nonatomic, copy) void(^onDealloc)(void);
@end
@implementation Watcher
- (void)dealloc
{
NSLog(@"Dealloc Watcher");
self.onDealloc();
}
@end
@implementation AppDelegate
- (void)crashIfICan
{
Source *source = [[Source alloc] init];
Target *target = [[Target alloc] init];
Watcher *watcher = [[Watcher alloc] init];
target.unsafeSource = source;
objc_setAssociatedObject(source, &Key, watcher, OBJC_ASSOCIATION_RETAIN);
__block BOOL hitDealloc = NO;
__weak Target *weakTarget = target;
watcher.onDealloc = ^{
// Don't need to sleep in here because by the time this is called
// it's too late for the "unsafeSource". It's already "deallocated".
hitDealloc = YES;
weakTarget.unsafeSource = nil;
};
__block int preDealloc = NO;
__block int postDealloc = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
while ([target description] && [target.unsafeSource description]) {
if (hitDealloc) {
// While sleeping in the associated object's dealloc, continue to
// hit the source object via an unsafe reference.
postDealloc++;
} else {
preDealloc++;
}
}
});
// The problem is when you rely on the associated object to nil out unsafe
// references. Typically, you wouldn't manually nil out the "watcher"
// object (or even have access to it). Relying on the source getting deallocated
// to deallocate the "watcher" to then nil out unsafe references is potentially
// dangerous. Below we have a race condition which often crashes:
//watcher = nil;
source = nil;
NSLog(@"Finished with counts - preDealloc: %d, postDealloc: %d", preDealloc, postDealloc);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self crashIfICan];
return YES;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment