-
-
Save cconstable/c1807469bebf74c08d01 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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