Меня смущает случайный сбой, который я наблюдаю, что, согласно инструменту Zombies, вызвано чрезмерным выпуском некоторых значений словаря. Когда я просматриваю историю объектов для одного из этих чрезмерно выпущенных объектов в «Инструментах», я вижу, что его счетчик сохранения падает прямо с +2 до 0 на одном этапе. (Посмотрите на скриншоты в конце поста). Мне непонятно, как это вообще возможно.
Я должен сказать, что я вижу этот сбой только при профилировании с помощью инструментов, поэтому я полагаю, что это может быть ошибка Apple, но, вероятно, безопаснее предположить, что это ошибка пилота, которую инструменты просто выявляют.
В любом случае, я создаю CFDictionary, который содержит некоторые объекты Core Foundation (CFStrings и CFNumbers), а затем я привожу его к NSDictionary* и передаю методу Objective-C. Упрощенная версия моего кода приведена ниже:
// 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];
}
Я думал, что str1
, str2
и num1
будут рассматриваться как объекты Objective-C и, следовательно, будут захвачены и автоматически сохранены, когда блок в -receiveDictionary:
копируется вызовом dispatch_async
, и освобождаются, когда этот блок освобождается. Действительно, кажется, что эти переменные захватываются и сохраняются блоком. Однако, изучая историю объекта для перевыпущенной CFString в Instruments, я вижу, что его счетчик ссылок увеличивается при копировании блока. Удивительно, но количество сохранений падает с +2 сразу до 0, когда блок освобождается (см. скриншот в конце поста); Я не знаю, как определить по трассировке стека, какой это блок. К тому времени, когда CFRelease
вызывается для словаря в блоке doStuff()
, некоторые из его значений уже освобождены, и программа аварийно завершает работу.
Так откуда взялся дополнительный релиз? Как может счетчик удержания объекта сразу упасть с +2 до 0, как указывает Instruments?
По прихоти я заставил второй блок сохранить весь словарь, например:
dispatch_async(myOtherQueue, ^{
[database executeUpdate:@"INSERT INTO blah (x,y,z) VALUES (?, ?, ?)", str1, str2, num1];
[dict self];
});
Это, кажется, заставляет исчезнуть крах; инструменты перестают сообщать о зомби, по крайней мере. Однако я не могу понять, почему это работает; конечно, я просто для того, чтобы блок сохранял интересующие меня значения словаря, а не весь словарь. Так что же происходит?
Instruments перечисляет следующую историю объекта для зомби CFString с количеством сохраненных объектов. Я включил скриншоты для интересных событий.
#0 +1 CFString создана
#1 +2 CFString добавлена в словарь
#2 +1 CFString освобождена
#3 +2 CFString сохраняется при копировании блока в -receiveDictionary:
#4 +0 Что ...? Счетчик удержания объекта сразу упал с +2 до 0!
#5 -1 CFDictionary выпущен, что приводит к сбою