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

1

7

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
Borges answered 25/10, 2015 at 6:25 Comment(10)
It would help if you edited your question to include the contents of the block, or at least a few lines following line 2367 of MyDataManager.m.Unquestioning
Seems like your self is deallocated at the time block is triggeredBolide
Since there is no special declaration of self in evidence, the block should have a strong reference to self and thus prevent it from being deallocated.Unquestioning
What is the OS version in the crash report (near the top of the file)? E.g. iOS 9.0.2 (13A452). What is the code type? E.g. ARM-64 (Native). What is the binary image listed for libsystem_c.dylib (near the end of the file)? E.g. 0x199098000 - 0x199118fff libsystem_c.dylib arm64 <5052939437823b09a7b068807808eff2> /usr/lib/system/libsystem_c.dylibUnquestioning
That's just my guess, you can enable zombie from the instrument and check what's actually happening.Bolide
I would weakify self and then strongify just to make sure no retain cycle will be created, which may cause in hard to debug issuesCimbura
@robmayoff OK I have added information about the OS and the binary image from libsystem_c.dylib.Borges
Did you end up fixing this @esilver?Tini
I ended up re-writing that section and added a ton of diagnostic info...and then the crashing stack went away. So, in summary, no I did not solve it.Borges
I see the problem you are talking about, I have no idea how self 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 in dispatch_async?Tini
P
2

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.

Puccini answered 25/10, 2015 at 11:38 Comment(8)
1. Please explain why you think sending a message to a weak variable is ever appropriate unless you are sure someone else holds a strong reference (in which case using the weak reference is nonsense). 2. myMethod is not on the call stack. It's a block inside the message (most likely the block calling cleanupMethod).Benelux
1. Because of exactly this situation the OP has. App won't crash after sending message to weak reference. 2. Yes, you are right about this one, I didn't notice name difference.Puccini
@Puccini thank you so much for your thoughts here! I have added more details about my code -- it's not clear to me that this is a strong/weak ref issue. Can you take a look at my expanded question and LMK if you think this still could be the culprit? Thank you!Borges
@Puccini OK true, but shouldn't self and [MyDataManager mainStore] always refer to the same value? I just added an assert(self == [MyDataManager mainStore]); at the top of my block and it is passing...Borges
No. 'Cause you can have uninitialized self. But you don't use self there, you have to use class method. I guess that on your viewDidLoad you are accessing the sharedInstance directly while it is not being initialized. You have to use your class method, otherwise what is the point of it.Puccini
Sorry, I'm still confused. I believe it to be true that all clients of this singleton must use [MyDataManager mainStore]. The singleton itself may always use either self or [MyDataManager mainStore]. If you are saying that is not true, that's new info to me...perhaps I should open up a new S.O. post on this specific question? Furthermore, what does it mean for self to be uninitialized when it originally is pointing to [MyDataManager mainStore]?Borges
I was talking about another part of your code - about viewDidLoad.Puccini
Just try to use class method there and try to reproduce the crash.Puccini

© 2022 - 2024 — McMap. All rights reserved.