2

I have some code which is similar to the following code:

dispatch_queue_t queue          = dispatch_queue_create("", 0);
dispatch_queue_t inner_queue    = dispatch_queue_create("", 0);
dispatch_async(queue,
^{
    NSAutoreleasePool* autoreleasePool = [[NSAutoreleasePool alloc] init];
    NSArray* objects = [self.otherObject getObjectsFromSlowQuery];

    [objects enumerateObjectsWithUsingBlock:^(id anObject, NSUInteger idx, BOOL *stop) 
    {
        [anObject prepare];
        dispatch_async(inner_queue,
        ^{
            InnerObject* innerObject = anObject.innerObject;
            [self.helper doSomethingExpensiveWithObject:innerObject];
        });
        dispatch_sync(self.syncQueue,
        ^{
             [self insertIntoCollection:anObject];
        });
    }];
    dispatch_release(inner_queue);
    [autoreleasePool drain];
});
dispatch_release(queue);

Where [anObject.innerObject] is a nonatomic property.

I got a crash report from a user which shows an EXC_BAD_ACCESS in objc_msgSend() when trying to access a property of innerObject within the doSomethingExpensiveWithObject: call.

At first I was thinking that perhaps the autoreleasePool was drained so the innerObject instance was released before returning from doSomethingExpensiveWithObject: but as far as I know anObject should be retained by the inner dispatch_async call which should also keep the innerObject alive.

What am I missing?

Monolo
  • 18,205
  • 17
  • 69
  • 103
Gabriel Ayuso
  • 207
  • 3
  • 13
  • 1
    Does the code work without the queues? Also, you [don't need to allocate an NSAutoreleasePool](http://stackoverflow.com/questions/4141123/do-you-need-to-create-an-nsautoreleasepool-within-a-block-in-gcd) here. – DarkDust Jan 20 '12 at 17:47
  • Blocks don't effect the retain count of the objects they capture. – user1139069 Jan 20 '12 at 18:07
  • @DarkDust This code is called form the main thread. The code hits the file system and performs web requests so I'm doing it in the background and gradually updating the UI. I added the `NSAutoreleasePool` because many object are allocated within the expensive operations. – Gabriel Ayuso Jan 20 '12 at 18:12
  • 2
    @user1139069: [They do.](http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW4) Which is why the `__block` is needed to turn that off (and make the block be able to modify the variable). – DarkDust Jan 20 '12 at 18:24
  • `__block` only changes whether the variable is constant or not. It has nothing to do with it's retain count. – user1139069 Jan 20 '12 at 18:31
  • 1
    @user1139069 `dispatch_async` calls `Block_copy` which retains the variables within the block scope. – Gabriel Ayuso Jan 20 '12 at 18:49
  • Given that `[self.otherObject getObjectsFromSlowQuery]` is the reason why the `NSAutoreleasePool` is there in the first place, I will move the `NSAutoreleasePool` inside `[self.otherObject getObjectsFromSlowQuery]` so it doesn't affect the rest of the code. – Gabriel Ayuso Jan 20 '12 at 18:57
  • What are the all the properties on innerObject? If you were setting innerObject with an auto released object in -prepare, that object will get eaten by the autorelease pool even if anObject is retained. – user1139069 Jan 20 '12 at 19:17
  • 2
    There's no particular evidence that the autorelease pool has anything to do with the crash. But using `dispatch_async` means that the block may run on some other thread with its own autorelease pool (which might not be drained after each block is run). So you should create/drain a pool inside the `inner_queue` block if you think `doSomethingExpensiveWithObject:` needs it. – rob mayoff Jan 21 '12 at 07:13
  • @user1139069: No, `__block` doesn't change whether a variable is constant or not, `const` does that. [`__block` specifies whether a variable is mutable inside a block](http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html). And it *does* influence the retain count: If you don't use `__block` the variable gets retained. If you do use `__block` it will not get retained. This is why the `__block id unretainedSelf = self;` trick is often used. – DarkDust Jan 23 '12 at 08:43

1 Answers1

1

Instruments will make quick work of this - run with zombies and review the reference counts when it stops.

justin
  • 104,054
  • 14
  • 179
  • 226