I am trying to debug a crashing bug reported by many of my users in the field. All show me the same stack:
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 8
OS Version: iOS 9.1 (13B143)
Code Type: ARM (Native)
0 libsystem_kernel.dylib 0x392ccc84 0x392b8000 + 85124
1 libsystem_pthread.dylib 0x39370732 0x3936c000 + 18226
2 libsystem_c.dylib 0x39264f9a 0x3921a000 + 307098
3 libsystem_c.dylib 0x39264f2c 0x3921a000 + 306988
4 libsystem_c.dylib 0x392447ea 0x3921a000 + 174058
5 MyApp 0x000cb3e0 __69-[MyDataManager myMethod:]_block_invoke (MyDataManager.m:2367)
Line 2367 is simply:
2363: BOOL success = [db executeUpdate:@"INSERT INTO table (id, content) VALUES (?, ?)", message.remoteId, message.content];
2364: assert(success);
2365: DebugLog(@"DB Results %d", success);
2366:
2367: dispatch_async(dispatch_get_main_queue(), ^{
2368: [self cleanupMethod:args];
2369: });
While there certainly is code within that block, it's only 1 line long, and that code does not appear to be executed on this stack because otherwise I would see cleanupMethod
above myMethod
.
Edit: You can see that, just before the dispatch_async, there is an assert! I originally thought that this crash was due to the assert. But the line numbers never matched up -- the assert was many lines higher up (line 2364, not 2367) -- and when I tested it further, I saw that if the assert is triggered, my stack would not include the _block_invoke
that you can see appended to the end of the call to myMethod.
Can anyone suggest how a dispatch_async could trigger this behavior? Moreover, is there any way for me to symbolicate Apple's code in libsystem_c.dylib?
Binary image of libsystem_c.dylib:
0x3921a000 - 0x3927efff libsystem_c.dylib armv7 <0b5d65608e6f38448cd207fbd748d372> /usr/lib/system/libsystem_c.dylib
NOTE: the object in question is a global singleton, my "data manager" if you will. It handles network requests and stores state that may need to be shared between UIViewControllers. It is declared originally as follows:
+ (MyDataManager *)mainStore {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
I understand the consequences of the object being deallocated when my cleanupMethod:args
method is called...but I had thought my global singleton would always be around and thus always safe to call in the way I do in my code? I am furthermore not concerned about retain cycles since again, this is supposed to be a global singleton.
Is this code sample below OK to do?
@interface MyDataManager
@end
@implementation MyDataManager
+ (MyDataManager *)mainStore {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (void)myMethod {
NSDictionary *args = @{...}
...
dispatch_async(dispatch_get_main_queue(), ^{
[self cleanupMethod:args];
});
}
- (void)cleanupMethod:(id)args {
...
}
@end
@interface MyViewController : UIViewController
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[MyDataManager sharedInstance] myMethod];
}
@end
Looks like the problem is in strong reference to self
, which crashes your app, apparently when calling self
that was deallocated.
This code will make a new variable that is going to store a weak reference to self
, which should resolve the issue:
__weak typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf cleanupMethod:args];
});
Some would say that you would have to use __strong typeof(weakSelf)strongSelf = weakSelf;
here as well, to make a strong reference to the weak reference, to avoid strong reference cycle but to keep the self
alive, but I would prefer not to do it here and in case self
would be nil
by the time of execution of the block - messages to nil
are perfectly fine in objective-c, so nothing will happen.
Plus the stack trace of what happens before the line
0x000cb3e0 __69-[MyDataManager myMethod:]_block_invoke (MyDataManager.m:2367)
would definitely help to diagnose an issue.
EDIT: Well, you don't seem to use the class method mainStore
when accessing your shared object. Maybe that is the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With