I am confused by the accidental accident that I see, which, according to the Zombies tool, is caused by the excessive release of some dictionary values. When I look at the history of objects for one of these overridden objects in the Tools, I see that its hold counter drops from +2 to 0 at one stage. (Take a look at the screenshots at the end of the post). I don’t understand how this is possible.
I have to say that I see only this crash when profiling with tools, so I believe that this may be an Apple error, but it is probably safer to assume that this is a pilot error that the tools simply expose.
In any case, I create a CFDictionary that contains some Core Foundation objects (CFStrings and CFNumbers), and then I pass it to NSDictionary * and pass it to the Objective-C method. The following is a simplified version of my 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]; }
I thought that str1
, str2
and num1
would be treated as Objective-C objects and therefore would be captured and automatically saved when the block in -receiveDictionary:
was copied by the dispatch_async
call, and freed when that block was freed. Indeed, these variables seem to be captured and stored by the block. However, examining the history of objects for an overridden CFString in Tools, I can see that its reference count increases as the block is copied. It's amazing that its number of deductions drops from +2 to 0 when the block is freed (see. Screenshot at the end of the message); I don't know how to tell from the stack trace that blocks this. By the time CFRelease
is called in the dictionary in a block in doStuff()
, some of its values are already freed, and the program will work.
So where did the extra release come from? How can the object counter be kept on the straight side from +2 to 0, as the device shows?
On a whim, I forced the second block to save the entire dictionary, for example:
dispatch_async(myOtherQueue, ^{ [database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1]; [dict self]; });
It is like the accident will disappear; tools stop reporting zombies at least. I cannot understand for life why this works; I just have to make sure that the dictionary values that interest me are stored in the block, and not the entire dictionary. So what is going on?
The fixtures list the following object history for the Zombie CFString, while the object counter is saved. I have included screenshots for interesting events.
# 0 +1 Created by CFString
# 1 +2 CFString added to dictionary
# 2 +1 CFString released
# 3 +2 CFString is saved when copying a block to -receiveDictionary:
# 4 +0 What ...? The object’s held account fell straight from +2 to 0!
# 5 -1 CFDictionary exited, causing a crash