[Перевод] Циклические контейнеры в Objective-C

Некоторое время назад я написал этот код: 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 0×100211d80 Обработчик '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!

© Habrahabr.ru