什么可以在iOS9.1中的dispatch_async上触发SIGABRT?

esi*_*ver 7 grand-central-dispatch sigabrt ios dispatch-async

我正在尝试调试我的许多用户在该领域报告的崩溃错误.所有都显示我相同的堆栈:

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)
Run Code Online (Sandbox Code Playgroud)

2367行简单地说:

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: });
Run Code Online (Sandbox Code Playgroud)

虽然该块中肯定存在代码,但它只有1行长,并且该代码似乎不会在此堆栈上执行,否则我将cleanupMethod在上面看到myMethod.

编辑:你可以看到,就在dispatch_async之前,有一个断言!我原本以为这次崩溃是由于断言造成的.但是行号从来没有匹配 - 断言是更高的行(行2364,而不是2367) - 当我进一步测试它时,我看到如果断言被触发,我的堆栈将不包括_block_invoke你可以请参阅附加到myMethod调用的结尾.

任何人都可以建议dispatch_async如何触发此行为?此外,有没有办法在libsystem_c.dylib中象征Apple的代码?

libsystem_c.dylib的二进制图像:

0x3921a000 - 0x3927efff libsystem_c.dylib armv7  <0b5d65608e6f38448cd207fbd748d372> /usr/lib/system/libsystem_c.dylib
Run Code Online (Sandbox Code Playgroud)

注意:有问题的对象是全局单例,如果愿意,我的"数据管理器".它处理网络请求并存储可能需要在UIViewControllers之间共享的状态.它最初声明如下:

+ (MyDataManager *)mainStore {
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });

    return sharedInstance;
}
Run Code Online (Sandbox Code Playgroud)

我理解在cleanupMethod:args调用我的方法时对象被解除分配的后果......但是我曾经认为我的全局单例总是存在,因此我可以像在代码中一样安全地调用它?此外,我再也不关心保留周期,这应该是一个全球单身人士.

下面的代码示例可以吗?

@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
Run Code Online (Sandbox Code Playgroud)

Sob*_*man 2

看起来问题出在对 的强引用上self,这会导致您的应用程序崩溃,显然是在调用self已释放的应用程序时。此代码将创建一个新变量,该变量将存储对 的引用self,这应该可以解决该问题:

__weak typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf cleanupMethod:args];
});
Run Code Online (Sandbox Code Playgroud)

有人会说你也必须__strong typeof(weakSelf)strongSelf = weakSelf;在这里使用,对弱引用进行强引用,以避免强引用循环但保持活动状态self,但我不想在这里这样做,self以防nil万一块的执行时间 - 消息nil在 Objective-C 中完全正常,所以不会发生任何事情。

加上该行之前发生的事情的堆栈跟踪

0x000cb3e0 __69-[MyDataManager myMethod:]_block_invoke (MyDataManager.m:2367)
Run Code Online (Sandbox Code Playgroud)

肯定有助于诊断问题。

mainStore编辑:嗯,您在访问共享对象时似乎没有使用类方法。也许这就是问题所在。