Исключение NSZombie для NSEnumerator::nextObject (или nextObject касается своего запроса?)
Хорошо, я занимаюсь программированием в течение 12 лет, но я относительно неопытен в Obj-C - особенно в управлении памятью - и получаю ошибку, которая удивляет меня.
Вот блок кода:
// self.contained is an NSMutableSet
NSEnumerator *e = [self.contained objectEnumerator];
>> while (CCNode *node = [e nextObject]) {
if (!node.body || ![self validate:node]) {
[self.contained removeObject:node];
}
}
Я получаю _NSZombie_NSException
брошены на линии, обозначенной >>
, Хорошо, я понимаю, что это означает (всегда?), Что я получаю доступ к объекту, который был dealloc
редактор Чего я не понимаю, так это того, почему в этой строке происходит ошибка. Если node
Я получаю то, что было dealloc
ed, я ожидаю ошибку на следующей строке (например, когда я получаю доступ node.body
). Я не вижу, как NSEnumerator
сам объект вызывает проблему, так как он был создан непосредственно перед, и если это было self.contained
установить это должно было умереть на линии раньше, верно?
Итак, делает nextObject
на самом деле вызвать какой-то метод для извлеченного объекта (т.е. node
) что может вызвать исключение? Это могло бы объяснить это, но я бы не подумал, что это будет так. Или кто-нибудь может сказать мне, какой объект, скорее всего, зомби?
Это происходит очень периодически, у меня это было дважды на прошлой неделе или около того в процессе разработки, так что запуск инструмента-зомби вряд ли его поймает.
2 ответа
НЕ добавляйте и не удаляйте объекты из любой коллекции при ее перечислении.
Создайте копию и перечислите ее и манипулируйте оригиналом. Когда закончите, удалите копию.
Предполагая, что вы ARC, небольшое изменение сделает свое дело.
for (CCNode *node in [self.contained allObjects]) { // for contained being an NSSet
if (!node.body || ![self validate:node]) {
[self.contained removeObject:node];
}
}
for (CCNode *node in [NSArray arrayWithArray:self.contained]) { // for contained being an NSArray
if (!node.body || ![self validate:node]) {
[self.contained removeObject:node];
}
}
for (CCNode *node in [NSDictionary dictionaryWithDictionary:self.contained]) { // for contained being an NSDictionary. However, allObjects or allKeys may suit you well too depending on what you need.
if (!node.body || ![self validate:node]) {
[self.contained removeObject:node];
}
}
Если вы не используете ARC, добавьте вызов к методу autorelease для вновь созданного безымянного объекта коллекции. Как для (узел CCNode * в [[self.contained allObjects] autorelease])
Если вы предпочитаете явный перечислитель, то выполните:
NSEnumerator *e = [[self.contained allObjects] objectEnumerator];
while (CCNode *node = [e nextObject]) {
Из раздела "Использование перечислителя" раздела " Темы программирования коллекций":
Не безопасно удалять, заменять или добавлять элементы изменяемой коллекции при перечислении через нее. Если вам нужно изменить коллекцию во время перечисления, вы можете либо сделать копию коллекции и перечислить ее, используя копию, либо собрать информацию, которая вам требуется во время перечисления, и применить изменения позже.
С
[self.contained removeObject:node];
Вы удаляете объект из коллекции при перечислении его.