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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.