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