Циклические контейнеры в 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 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!

ссылка на оригинал статьи http://habrahabr.ru/post/258249/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *