iOS9.1中,什么可以触发dispatch_async导致SIGABRT错误?

7

我正在尝试调试一些用户在现场报告的崩溃错误。所有的报告都显示相同的堆栈:

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)

第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: });

虽然该块内确实有代码,但只有1行,而且该代码似乎没有在这个堆栈上执行,否则我会在myMethod之前看到cleanupMethod

编辑:您可以看到,在dispatch_async之前有一个assert!最初,我认为这个崩溃是由于assert引起的。但是行号从未匹配 - assert的行号很高(第2364行,而不是第2367行) - 并且当我进一步测试时,我发现如果触发了assert,我的堆栈将不包括您可以看到附加到对myMethod的调用末尾的_block_invoke

有人能建议我如何触发此行为的dispatch_async吗?此外,是否有任何方法可以符号化libsystem_c.dylib中的Apple代码?

libsystem_c.dylib的二进制映像:

0x3921a000 - 0x3927efff libsystem_c.dylib armv7  <0b5d65608e6f38448cd207fbd748d372> /usr/lib/system/libsystem_c.dylib

注意:在这里讨论的对象是一个全局单例,相当于我的“数据管理器”。它处理网络请求并存储可能需要在UIViewControllers之间共享的状态。最初声明的方式如下:
+ (MyDataManager *)mainStore {
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });

    return sharedInstance;
}

我知道当调用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

如果您编辑问题以包括块的内容,或者至少包括MyDataManager.m中2367行之后的几行代码,那将会很有帮助。 - rob mayoff
1
似乎您的“self”在块被触发时已被释放。 - Inder Kumar Rathore
由于没有特殊声明self的证据,该块应该对self有一个强引用,从而防止其被释放。 - rob mayoff
崩溃报告中的操作系统版本是什么(位于文件顶部附近)?例如 iOS 9.0.2 (13A452)。代码类型是什么?例如 ARM-64 (Native)libsystem_c.dylib 的二进制映像在文件末尾附近列出了什么?例如 0x199098000 - 0x199118fff libsystem_c.dylib arm64 <5052939437823b09a7b068807808eff2> /usr/lib/system/libsystem_c.dylib - rob mayoff
这只是我的猜测,你可以从仪器中启用“僵尸”并检查实际发生了什么。 - Inder Kumar Rathore
显示剩余5条评论
1个回答

2

看起来问题出在对self的强引用上,这导致你的应用程序崩溃,似乎是在调用已被释放的self时发生的。 以下代码将创建一个新变量,用于存储对self弱引用,这应该可以解决问题:

__weak typeof(self)weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf cleanupMethod:args];
});

有些人会说在这里也要使用__strong typeof(weakSelf)strongSelf = weakSelf;,以便对弱引用进行强引用,避免强引用循环但保持self的存活,但我更愿意不在这里执行此操作,如果在块执行时selfnil - 在objective-c中向nil发送消息是完全可以的,所以什么也不会发生。此外,在该行之前发生的堆栈跟踪也可以保留。
0x000cb3e0 __69-[MyDataManager myMethod:]_block_invoke (MyDataManager.m:2367)

一定会有帮助来诊断问题。

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


  1. 因为正是这种情况,OP才会出现。应用程序在向弱引用发送消息后不会崩溃。
  2. 是的,你对这个问题是正确的,我没有注意到名称的差异。
- Soberman
@Soberman非常感谢您在这里的想法!我已经添加了更多关于我的代码的细节 - 对我来说,这不是一个强/弱引用问题。您能否查看我的扩展问题并告诉我您是否认为这仍然可能是罪魁祸首?谢谢! - esilver
@Soberman 好的,没错,但是 self[MyDataManager mainStore] 不应该总是引用相同的值吗?我刚在我的代码块顶部添加了一个 assert(self == [MyDataManager mainStore]);,并且它通过了... - esilver
抱歉,我还是有点困惑。我相信所有这个单例的客户端都必须使用[MyDataManager mainStore]。单例本身可以始终使用self[MyDataManager mainStore]。如果您说这不是真的,那对我来说是新信息...也许我应该在这个具体问题上开一个新的S.O.帖子?此外,当self最初指向[MyDataManager mainStore]时,它未初始化是什么意思? - esilver
我在谈论你代码的另一部分 - 关于viewDidLoad。 - Soberman
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接