Исключение 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 Я получаю то, что было dealloced, я ожидаю ошибку на следующей строке (например, когда я получаю доступ 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];

Вы удаляете объект из коллекции при перечислении его.

Другие вопросы по тегам