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

Original author: AlexDenisov
  • Translation
Некоторое время назад я написал этот код:

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!
  • +13
  • 7.4k
  • 6
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 6

  • UFO just landed and posted this here
      0
      т.е. это ожидаемое поведение? то чего хотел программист?
      • UFO just landed and posted this here
      –1
      Да просто надо уже пользоваться методами типа map, flatMap и забыть про такие ошибки навсегда.
        0
        Тонко.
          –1
          Да нет, вполне себе нормально.

      Only users with full accounts can post comments. Log in, please.