Какие утечки делает автоматический подсчет ссылок в Objective-C не предотвращать или сводить к минимуму?

На платформах Mac и iOS утечки памяти часто вызваны невыпущенными указателями. Традиционно всегда было важно проверить ваши распределения, копии и сохранить, чтобы каждый из них имел соответствующее сообщение о выпуске.

Инструментальная цепочка, поставляемая с Xcode 4.2, вводит автоматический подсчет ссылок (ARC) с последней версией компилятора LLVM, что полностью устраняет эту проблему, компилятор в память - управляйте своими вещами для вас. Это довольно круто, и это сокращает много ненужного, мирского времени разработки и предотвращает много небрежных утечек памяти, которые легко исправить при правильном балансе сохранения/выпуска. Даже пулы автозаполнения должны управляться по-разному, когда вы включаете ARC для своих приложений Mac и iOS (так как вы больше не должны выделять свой собственный NSAutoreleasePool).

Но какие другие утечки памяти он не предотвращает, что я все еще должен следить за?

В качестве бонуса, каковы различия между ARC в Mac OS X и iOS и сборкой мусора в Mac OS X?

Ответ 1

Первичная проблема с памятью, о которой вам все еще нужно знать, - это сохранение циклов. Это происходит, когда один объект имеет сильный указатель на другой, но целевой объект имеет сильный указатель назад к оригиналу. Даже когда все другие ссылки на эти объекты удаляются, они все равно будут держаться друг за друга и не будут освобождены. Это также может происходить косвенно, цепочкой объектов, которые могут иметь последнее в цепочке, ссылаясь на более ранний объект.

Именно по этой причине существуют квалификаторы собственности __unsafe_unretained и __weak. Первый не сохранит объект, на который он указывает, но оставляет возможность уйти от объекта и указывать на плохую память, тогда как последняя не сохраняет объект и автоматически устанавливает себя на нуль, когда его цель освобождается. Из них __weak обычно предпочтительнее на платформах, которые его поддерживают.

Вы должны использовать эти квалификаторы для таких вещей, как делегаты, где вы не хотите, чтобы объект сохранял свой делегат и, возможно, приводил к циклу.

Еще одна важная проблема, связанная с памятью, - это обработка объектов и памяти Core Foundation, выделенных с помощью malloc() для таких типов, как char*. ARC не управляет этими типами, только Objective-C объектами, поэтому вам все равно придется иметь дело с ними самостоятельно. Типы Core Foundation могут быть особенно сложными, потому что иногда их нужно подключать к соответствующим объектам Objective-C и наоборот. Это означает, что управление должно быть передано обратно и обратно из ARC при перекрестке между типами CF и Objective-C. Некоторые ключевые слова, связанные с этим мостом, были добавлены, и Майк Эш прекрасно описывает различные случаи моста в его длинную запись ARC.

Кроме того, существует несколько других менее частых, но все же потенциально проблемных случаев, которые подробно описываются .

Большая часть нового поведения, основанного на сохранении объектов до тех пор, пока есть сильный указатель на них, очень похожа на сборку мусора на Mac. Однако технические основы очень разные. Вместо того, чтобы иметь сборщик мусора, который работает через регулярные промежутки времени, чтобы очистить объекты, на которые больше не ссылаются, этот стиль управления памятью основан на жестких правилах сохранения/выпуска, которые все мы должны соблюдать в Objective-C.

ARC просто выполняет повторяющиеся задачи управления памятью, которые мы должны были делать в течение многих лет, и выгружает их в компилятор, поэтому нам больше не придется беспокоиться о них. Таким образом, у вас нет проблем с остановкой или профилей пилообразной памяти, которые возникают на сборках мусора. Я испытал оба этих вопроса в своих сборниках Mac, и я очень хочу посмотреть, как они себя ведут в ARC.

Подробнее о сборке мусора против ARC см. этот очень интересный ответ Криса Латтнера в списке рассылки Objective-C, где он перечисляет многие преимущества ARC над сборкой мусора Objective-C 2.0. Я столкнулся с несколькими проблемами, которые он описывает.

Ответ 2

ARC не поможет вам с памятью, отличной от ObjC, например, если вы malloc() что-то, вам все равно нужно free() it.

ARC может быть обмануто performSelector:, если компилятор не может понять, что такое селектор (компилятор будет генерировать предупреждение об этом).

ARC также генерирует код, соответствующий соглашениям об именах ObjC, поэтому, если вы смешиваете код ARC и MRC, вы можете получить неожиданные результаты, если код MRC не делает то, что компилятор считает, что имена обещают.

Ответ 3

У меня возникли утечки памяти в моем приложении из-за следующих 4-х проблем:

  • Не отклонять NSTimers при отключении диспетчеров просмотра
  • Забыть удалить любых наблюдателей в NSNotificationCenter при отключении контроллера вида.
  • Сохранение сильных ссылок на себя в блоках.
  • Использование сильных ссылок на делегаты в свойствах контроллера.

К счастью, я наткнулся на следующее сообщение в блоге и смог их исправить: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/

Ответ 4

ARC также не будет управлять типами CoreFoundation. Вы можете "переманить" их (используя CFBridgingRelease()), но только если вы собираетесь использовать его как объект Objective-C/Cocoa. Обратите внимание, что CFBridgingRelease просто уменьшает коэффициент хранения CoreFoundation на 1 и перемещает его в Objective-C ARC.