0

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

-(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 如何在调度 async 内的调用之间被释放?我不明白这怎么可能。我知道很难就此提出建议,但任何调试建议都会有所帮助。我尝试打开 NSZombies,但我没有得到任何 Zombies。

4

1 回答 1

1

你问:

如何foo在 a 内的调用之间释放dispatch_async

它不应该,除非someMethod或者secondMethod是,他们自己,异步地做一些可能允许自动释放池在过渡期间被耗尽的事情。

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

如果你打开了僵尸,但你没有得到僵尸,那么我怀疑问题出在其他地方。坦率地说,我怀疑问题的根源在您为解决问题而简化示例代码的过程中被消除了:

其他一些意见/说明:

  1. 你声明foo是 a NSArray,但后来你又回来了NSObject。我会假设你的意思是NSObject贯穿始终。

  2. 你有一行代码说:

    dispatch_async(dispatch_get_main_queue(),0) {
    

    我只是假设这是一个错字并且您打算:

    dispatch_async(dispatch_get_main_queue(), ^{
    
  3. foo变量绝对应该在块dispatch_async内。为某些东西设置一个变量并没有什么意义__block(a)你没有在那个块之外引用一个块;(b) 对于您正在异步调度的块。

  4. secondMethod应该返回一个对象,autorelease就像您最初拥有它一样。(或者您可能希望更改secondMethod并从他们的名字someMethod开始,new以避免混淆,并在您最终搬到 ARC 时让自己的生活更轻松。)

  5. 如果您retainfoo对象,您还需要添加适当的release. 实际上,您的原始代码示例返回一个 +1 对象,然后再次保留它,将其撞到 +2,因此您需要两次release调用。

无论如何,纠正这些不同的问题,我最终得到以下结果,它不会产生异常:

- (IBAction)buttonTapped:(id)sender
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

        NSObject *foo = [self someMethod];
        [foo retain]; // no bad access here

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"foo = %@", foo);

            [foo release];
        });
    });
}

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

- (NSObject *)secondMethod
{
    return [[[NSObject alloc] init] autorelease];
}

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

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

于 2013-11-06T18:33:10.150 回答