【问题标题】:What could trigger a SIGABRT on dispatch_async in iOS9.1?什么会在 iOS9.1 中的 dispatch_async 上触发 SIGABRT?
【发布时间】:2016-01-24 10:29:54
【问题描述】:

我正在尝试调试我的许多用户在该领域报告的崩溃错误。都给我看相同的堆栈:

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 之前,有一个断言!我最初认为这次崩溃是由于断言造成的。但是行号从来没有匹配过——断言的行数更高(第 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

注意:有问题的对象是一个全局单例,如果你愿意的话,我的“数据管理器”。它处理网络请求并存储可能需要在 UIViewController 之间共享的状态。原声明如下:

+ (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 行之后的几行,这将有所帮助。
  • 好像你的self 在块被触发时被释放
  • 由于没有self 的特殊声明,因此该块应该具有对self 的强引用,从而防止它被释放。
  • 崩溃报告中的操作系统版本是什么(文件顶部附近)?例如。 iOS 9.0.2 (13A452)。什么是代码类型?例如。 ARM-64 (Native)。为libsystem_c.dylib(接近文件末尾)列出的二进制图像是什么?例如。 0x199098000 - 0x199118fff libsystem_c.dylib arm64 &lt;5052939437823b09a7b068807808eff2&gt; /usr/lib/system/libsystem_c.dylib
  • 这只是我的猜测,您可以从仪器启用zombie 并检查实际发生的情况。

标签: ios grand-central-dispatch sigabrt dispatch-async


【解决方案1】:

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

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

有些人会说你也必须在这里使用__strong typeof(weakSelf)strongSelf = weakSelf;,以对弱引用进行强引用,避免强引用循环但保持self活着,但我不想这样做它在这里,以防 self 在执行块时将是 nil - 到 nil 的消息在 Objective-c 中非常好,所以什么都不会发生。

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

0x000cb3e0 __69-[MyDataManager myMethod:]_block_invoke (MyDataManager.m:2367)

肯定有助于诊断问题。

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

【讨论】:

  • 1.请解释为什么您认为向弱变量发送消息是合适的,除非您确定其他人持有强引用(在这种情况下使用弱引用是无稽之谈)。 2. myMethod 不在调用栈上。它是消息中的一个块(很可能是调用 cleanupMethod 的块)。
  • 1.由于正是这种情况,OP有。向弱引用发送消息后应用不会崩溃。 2.是的,你说得对,我没有注意到名字的不同。
  • @Soberman 非常感谢您在这里的想法!我添加了有关我的代码的更多详细信息——我不清楚这是一个强/弱参考问题。如果您认为这仍然可能是罪魁祸首,您能看看我的扩展问题和 LMK 吗?谢谢!
  • @Soberman OK 是的,但 self[MyDataManager mainStore] 不应该总是引用相同的值吗?我刚刚在我的街区顶部添加了一个assert(self == [MyDataManager mainStore]);,它正在通过...
  • 没有。因为你可以有未初始化的自我。但是你不在那里使用 self ,你必须使用类方法。我猜在您的 viewDidLoad 上,您正在直接访问 sharedInstance 而它没有被初始化。你必须使用你的类方法,否则它有什么意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-16
  • 2012-10-22
  • 2020-01-09
  • 1970-01-01
  • 2012-03-31
相关资源
最近更新 更多