Некоторое время назад я написал этот код:
Заметили проблему? Я — нет.
После запуска программы я словил крэш:
Обработчик 'environments' ожидал 'XCCEnvironment', а получил 'NSMutableArray'.
Поначалу было непонятно почему это произошло, но когда я присмотрелся к коду, то заметил, что я просто кладу массив в этот же массив:
Документация ничего не говорит о поведении коллекций в таких ситуациях, единственный полезный материал который я нашел по этому поводу — статья Майка Эша (Mike Ash) Let's break Cocoa.
Статья утверждает что мутабельные массивы, словари и множества сходят с ума если создать так называемый циклический контейнер. Кроме того при включенном ARC появляется утечка памяти — коллекция удерживает сама себя.
Обычно разработчики не кладут коллекцию внутрь самой себя. Это так же справедливо, как и утверждение, что программисты не разыменовывают нулевые указатели — это все еще происходит и, вероятно, это не то чего ожидает программист.
Я был абсолютно уверен что clang может предотвратить эту ошибку, но не нашел никаких параметров/настроек которые включают эту проверку.
В конце концов я решил добавить эту проверку. Реализация заняла пару вечеров, но теперь она находится в trunk.
Патч включает в себя проверку следующих коллекций:
и показывает предупреждение если вы пытаетесь добавить коллекцию внутрь ее самой.
Предупреждение может быть включено/выключено посредством флагов '-wobjc-circular-container'/'-wno-objc-circular-container' соответственно, хотя оно включено по-умолчанию.
Версия clang из trunk'а содержит эту функциональность, но она еще недоступна в Xcode, я полагаю что появится она там после следующего мажорного релиза, может быть через год.
Как бы то ни было, иметь компилятор с открытым кодом очень здорово: его можно чинить, улучшать и делать свою жизнь и жизнь других людей немного проще.
Happy hacking!
NSMutableArray *environments = [NSMutableArray new];
for (NSString *key in [dictionary allKeys]) {
XCCEnvironment *environment = [[XCCEnvironment alloc] initWithName:key
parameters:dictionary[key]];
[environments addObject:environments];
}
return environments;
Заметили проблему? Я — нет.
Проблема
После запуска программы я словил крэш:
[__NSArrayM someSelector]: unrecognized selector sent to instance 0x100211d80
Обработчик 'environments' ожидал 'XCCEnvironment', а получил 'NSMutableArray'.
Поначалу было непонятно почему это произошло, но когда я присмотрелся к коду, то заметил, что я просто кладу массив в этот же массив:
// ...
NSMutableArray *environments = [NSMutableArray new];
// ...
[environments addObject:environments];
// ...
Документация ничего не говорит о поведении коллекций в таких ситуациях, единственный полезный материал который я нашел по этому поводу — статья Майка Эша (Mike Ash) Let's break Cocoa.
Статья утверждает что мутабельные массивы, словари и множества сходят с ума если создать так называемый циклический контейнер. Кроме того при включенном ARC появляется утечка памяти — коллекция удерживает сама себя.
Решение
Обычно разработчики не кладут коллекцию внутрь самой себя. Это так же справедливо, как и утверждение, что программисты не разыменовывают нулевые указатели — это все еще происходит и, вероятно, это не то чего ожидает программист.
Я был абсолютно уверен что clang может предотвратить эту ошибку, но не нашел никаких параметров/настроек которые включают эту проверку.
В конце концов я решил добавить эту проверку. Реализация заняла пару вечеров, но теперь она находится в trunk.
Патч включает в себя проверку следующих коллекций:
- NSMutableArray
- NSMutableDictionary
- NSMutableSet
- NSMutableOrderedSet
- NSCountedSet
и показывает предупреждение если вы пытаетесь добавить коллекцию внутрь ее самой.
Предупреждение может быть включено/выключено посредством флагов '-wobjc-circular-container'/'-wno-objc-circular-container' соответственно, хотя оно включено по-умолчанию.
Заключение
Версия clang из trunk'а содержит эту функциональность, но она еще недоступна в Xcode, я полагаю что появится она там после следующего мажорного релиза, может быть через год.
Как бы то ни было, иметь компилятор с открытым кодом очень здорово: его можно чинить, улучшать и делать свою жизнь и жизнь других людей немного проще.
Happy hacking!
mifki
Да ничего там страшного нет, что не работают пара методов (что вполне логично), это еще не «сходят с ума».
1101_debian Автор
т.е. это ожидаемое поведение? то чего хотел программист?
mifki
Мало ли, что программист хочет, есть много и других вещей, которые нельзя делать.
Полноценная поддержка циклических контейнеров в этих методах заметно замедлила бы их и требовала бы дополнительной памяти, так что логично и хорошо, что её там нет. Но это не значит, что такие контейнеры по определению не поддерживаются и их нельзя использовать, если очень хочется.
А патч, наверное, кому-то пригодится, я ничего против не имею.