Note that there are some explanatory texts on larger screens.

plurals
  1. POOverrelease issue with block-captured objects; retain count jumps straight from +2 to 0!
    text
    copied!<p>I'm confused by an occasional crash that I'm seeing, which, according to the Zombies instrument, is caused by the over-release of some dictionary values. When I look at the object history for one of these overreleased objects in Instruments, I see that its retain count drops straight from +2 to 0 at one stage. (Take a look at the screenshots at the end of the post). It's not clear to me how this is even possible. </p> <p>I should say that I only see this crash when profiling with Instruments, so I suppose it could be an Apple bug, but it's probably safer to assume that it's pilot error, which Instruments is merely exposing.</p> <p>Anyways, I'm constructing a CFDictionary that contains some Core Foundation objects (CFStrings and CFNumbers), and I then cast this to a NSDictionary* and pass it to an Objective-C method. A simplified version of my code is below:</p> <pre><code>// creates a CFDictionary containing some CFStrings and CFNumbers void doStuff() { CFDictionaryRef myDict = CreateMyDictionaryContainingCFTypes(); dispatch_async(myQueue, ^{ [someObject receiveDictionary:(NSDictionary*)myDict]; CFRelease(myDict); // this line causes a crash. The Zombies instrument // claims a CFString value contained in this // dictionary has already been freed. }); } // ... - (void)receiveDictionary:(NSDictionary*)dict { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSString* str1 = [dict objectForKey:@"key1"]; NSString* str2 = [dict objectForKey:@"key2"]; NSNumber* num1 = [dict objectForKey:@"key3"]; dispatch_async(myOtherQueue, ^{ [database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1]; }); [pool drain]; } </code></pre> <p>I had thought that <code>str1</code>, <code>str2</code> and <code>num1</code> would be seen as Objective-C objects, and would therefore be captured and automatically retained when the block in <code>-receiveDictionary:</code> is copied by the <code>dispatch_async</code> call, and released when that block is released. Indeed, these variables do seem to be captured and retained by the block. However, examining the object history for an overreleased CFString in Instruments, I can see that its reference count is being incremented when the block is copied. Perplexingly, its retain count drops from +2 straight to 0 when a block is released (see the screenshot at the end of the post); I don't know how to tell from the stack trace which block this is. By the time <code>CFRelease</code> is called on the dictionary in the block in <code>doStuff()</code>, some of its values have been deallocated already, and the program crashes.</p> <p>So where did the extra release call come from? How can an object's retain count drop straight from +2 to 0, as Instruments indicates?</p> <p>On a whim, I forced the second block to retain the entire dictionary, like so:</p> <pre><code>dispatch_async(myOtherQueue, ^{ [database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1]; [dict self]; }); </code></pre> <p>This seems to make the crash disappear; instruments stops reporting zombies, at least. I cannot for the life of me see why this works, though; surely I just to ensure that the block retains the dictionary values that I'm interested in, not the entire dictionary. So what's going on?</p> <hr> <p>Instruments lists the following object history for the zombie CFString, with the object's retain count. I've included screenshots for the interesting events.</p> <p>#0 +1 CFString is created<br> #1 +2 CFString added to dictionary<br> #2 +1 CFString released<br> #3 +2 <a href="http://dl.dropbox.com/u/360795/stackoverflow/Screen%20shot%202011-05-14%20at%204.30.07%20PM.png" rel="nofollow">CFString is retained when the block in <code>-receiveDictionary:</code> is copied</a><br> #4 +0 <a href="http://dl.dropbox.com/u/360795/stackoverflow/Screen%20shot%202011-05-14%20at%204.30.12%20PM.png" rel="nofollow">What the...? The object's retain count dropped straight from +2 to 0!</a><br> #5 -1 <a href="http://dl.dropbox.com/u/360795/stackoverflow/Screen%20shot%202011-05-14%20at%204.30.14%20PM.png" rel="nofollow">CFDictionary is released, causing crash</a></p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload