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
self
is deallocated at the time block is triggered – Bolideself
in evidence, the block should have a strong reference toself
and thus prevent it from being deallocated. – UnquestioningiOS 9.0.2 (13A452)
. What is the code type? E.g.ARM-64 (Native)
. What is the binary image listed forlibsystem_c.dylib
(near the end of the file)? E.g.0x199098000 - 0x199118fff libsystem_c.dylib arm64 <5052939437823b09a7b068807808eff2> /usr/lib/system/libsystem_c.dylib
– Unquestioningzombie
from the instrument and check what's actually happening. – Bolideself
in that context could have been released, and now I'm very curious too. I'm sure you've tried everything at this point, too. Not sure if you said this somewhere-- but do you remember if you ever tried calling-cleanUpMethod
synchronously; without wrapping the call indispatch_async
? – Tini