How to set up proper object teardown when using GCD and calling back to main thread?
Consider this setup:

Object A creates object B for doing some work, and sets itself as B's delegate to be informed of work progress.

B does some work with GCD blocks, and signals back to A with the delegate method about work completion. A wants to tear down (release) B upon work completion.

In code terms:

Object A:

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

Object 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];
            });
            
        });
    }


PROBLEM: calling [b release] inside object A causes a crash. I think it's because the dispatch queue/background code is still running when A tries to release B.

QUESTION: how do I properly set up the objects and signaling in this case, to make sure that A only destroys B when all the background work has been completed?
How to set up proper object teardown when using GCD and calling back to main thread?
===

Consider this setup:

Object A creates object B for doing some work, and sets itself as B's delegate to be informed of work progress.

B does some work with GCD blocks, and signals back to A with the delegate method about work completion. A wants to tear down (release) B upon work completion.

In code terms:

Object A:

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

Object 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];
            });
            
        });
    }


PROBLEM: calling [b release] inside object A causes a crash. I think it's because the dispatch queue/background code is still running when A tries to release B.

QUESTION: how do I properly set up the objects and signaling in this case, to make sure that A only destroys B when all the background work has been completed?