Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What could trigger a SIGABRT on dispatch_async in iOS9.1?

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
like image 366
esilver Avatar asked Oct 25 '15 06:10

esilver


1 Answers

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.

like image 174
Soberman Avatar answered Nov 05 '22 22:11

Soberman