使用dispatch_async的奇怪的bad_access

时间:2013-11-06 17:18:57

标签: ios memory-management grand-central-dispatch

使用调度异步时,我遇到了一个非常奇怪的错误访问错误。我设法将它减少到我的程序中的这段代码。

-(void)buttonTapped:(id)sender {

    __block NSArray*foo = nil;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        //Foo was initially declared here but then moved it outside.
        foo = [self someMethod];
        [foo retain]; // bad access here. Why ?
        dispatch_async(dispatch_get_main_queue(),0) {
            // doesnt matter what happens here
    }); });
}

-(id)someMethod 
{
    return [self secondMethod];
}

-(id)secondMethod
{
    // was initially returning an autoreleased object from here. Changed it
    // to eliminate that as source of the error.
    id newThing = [[NSObject alloc] init];
    return newThing;
}

代码最初看起来不像这样,但现在就是这样。包括分配一个虚拟NSObject。

如何在调度异步中的调用之间释放foo?我不明白这是怎么可能的。我知道很难说明这是什么,但任何调试建议都会有所帮助。我尝试打开NSZombies,但我没有得到任何僵尸。

1 个答案:

答案 0 :(得分:1)

你问:

  

foo如何在dispatch_async内的来电之间释放?

除非someMethodsecondMethod本身,否则不应该异步执行某些操作,这可能会导致自动释放池在过渡期间耗尽。

  

我尝试打开NSZombies,但我没有得到任何僵尸。

如果你已经打开僵尸并且你没有得到一个僵尸,那么我怀疑问题在其他地方。坦率地说,我怀疑在为了问题的目的而简化示例代码的过程中,问题的根源被消除了:

其他一些观察/澄清:

  1. 您宣布fooNSArray,但之后您将返回NSObject。我会假设你的意思是NSObject。{/ p>

  2. 您有一行代码说:

    dispatch_async(dispatch_get_main_queue(),0) {
    

    我只是假设这是一个错字而你打算:

    dispatch_async(dispatch_get_main_queue(), ^{
    
  3. foo变量绝对应该位于dispatch_async块内。对于(a)你没有在块的外部引用块的某个东西({1}}变量没有任何意义。 (b)对于一个块,你是异步调度的。

  4. __block应返回secondMethod个对象,因为您显然最初拥有它。 (或者您可能希望将autoreleasesecondMethod改为以someMethod开头,以避免混淆,并在您最终迁移到ARC时让自己的生活更轻松。)

  5. 如果new retain对象,您还需要添加相应的foo。实际上,您的原始代码示例会返回+1对象,然后再次保留它,将其提升为+2,因此您需要进行两次release次调用。

  6. 无论如何,纠正这些不同的问题,我最终得到以下内容,这不会产生异常:

    release

    此外,我建议,特别是在使用手动保留和释放(MRR)时,通过静态分析仪(Xcode“产品”菜单上的“分析”)运行它,并确保您有一个干净的健康状况。 (它本来会指出我提到的一些问题。)它并不完美,但它在识别问题方面非常擅长。

    但是,简而言之,上面的代码很好,如果您仍然遇到异常,请使用重现异常的工作代码更新您的问题。