如何在使用GCD并回调主线程时设置正确的对象拆解?

时间:2011-04-16 21:13:33

标签: ios grand-central-dispatch

考虑这个设置:

对象A创建对象B以进行某些工作,并将自身设置为B的委托以获知工作进度。

B对GCD块执行一些操作,并使用委托方法向工作完成发回信号。 A想要在工作完成时拆除(释放)B。

在代码中:

对象A:

B *b = [[B alloc] init];
b.delegate = self;
[b doSomeWork];
- (void) didSomeWorkFromB:(B *)b {
    [b release];
    b = nil;
}

对象B:

- (void) doSomeWork {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        doSomeWork();

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Work is complete.");
            [self.delegate didSomeWorkFromB:self];
        });

    });
}

问题:在对象A内调用[b release]会导致崩溃。我认为这是因为当A试图释放B时,调度队列/后台代码仍在运行。

问题:在这种情况下,如何正确设置对象和信号,以确保A在所有后台工作完成后仅销毁B?

2 个答案:

答案 0 :(得分:1)

虚假问题。它实际上按预期工作,上面的代码不会崩溃。崩溃是由一些不相关的代码引起的。

答案 1 :(得分:0)

你是对的,代码按原样运行。但它不必要地复杂化。

您可以让B&#39 {s} doSomeWork保留自己(通过在[self retain]中明确调用[self release]doSomeWork或仅通过引用selfdispatch_async块中,它将为我们保留它),并在调用A后立即清除doSomeWork,因此didSomeWorkFromB不需要进一步清理

此模式在iOS中非常常见。例如,如果您查看NSURLConnection的许多常见实现,因为它在连接下载时保留自身,并在连接完成后自行释放,我们通常不会保留对连接的引用并在connectionDidFinishLoading清理它。只需让引用计数内存管理的魔力为您处理一切。

A

- (void) test
{
    B *b = [[B alloc] init];
    b.delegate = self;
    [b doSomeWork];
    [b release];    // you could also autorelease above, but I just wanted to make it more explicit for the purposes of the demonstration
}

- (void) didSomeWorkFromB:(B *)b
{
    // [b release]; // don't need to release it ... we already did
    // b = nil;     // certainly don't need to nil local reference ... this does nothing useful in any scenario
}

B

- (void) dealloc
{
    // let's log this so we can see when it's deallocated

    NSLog(@"%s", __FUNCTION__);

    [super dealloc];
}

- (void) doSomeWork
{
    // [self retain];  // you could manually retain if you want

    NSLog(@"%s", __FUNCTION__);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(10);

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Work is complete.");

            [self.delegate didSomeWorkFromB:self];

            // [self release]; // and if you manually retained, you'd manually release, too
        });
    });
}

在我看来,这种方法(在调用release之后立即拥有A doSomeWork B)更加健壮,更紧密地协调对象的平衡清理。我还认为,当你考虑最终转向ARC时,这会让你更好。