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


235

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

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

Но какие еще утечки памяти не мешают мне следить за этим?

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

Ответы:


262

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

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

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

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

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

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

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

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


2
Спасибо за подробный ответ. У меня была та же проблема, когда я определил делегата в _unsafe_unretained и получил сбой моего приложения, позже исправил его, изменив на сильный, но теперь у него есть утечка памяти. Итак, я изменил его на слабый и работает как шарм.
Чатурам

@ichathura Ух ты! Вы спасли меня от грязи ARC. Я столкнулся с тем же сбоем при использовании CMPopTipView.
Нянлян

@BradLarson: «у вас нет проблем с остановкой или профилей памяти пилообразной памяти, возникающих на платформах для сбора мусора». Я ожидал бы худших профилей остановки и пилообразной памяти от восстановления на основе области и намного худшей производительности от подсчета ссылок, поэтому я хотел бы увидеть реальное сравнение.
Джон Харроп

Брэд, ссылка от Криса Латтнера мертва . Я не на 100%, но я нашел эту другую ссылку. Что я думаю, что вы хотели ссылку: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/...
Мед

1
@Honey - Спасибо за указание на это. Ссылка, на которую вы ссылаетесь, немного отличается, но я заменил мертвую ссылку на заархивированную версию исходного сообщения. Он находится в архивах списков рассылки, которые где-то должны быть доступны, но я посмотрю, смогу ли я найти их новое местоположение.
Брэд Ларсон

14

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

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

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


7

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

  1. Не делает недействительными NSTimers при отклонении контроллеров представления
  2. Забыв удалить любых наблюдателей в NSNotificationCenter при удалении контроллера представления.
  3. Держать сильные ссылки на себя в блоках.
  4. Использование сильных ссылок на делегатов в свойствах контроллера представления

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


0

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


0

Xcode 9 предоставляет отличный инструмент для поиска подобных проблем. Он называется: « Граф отладочной памяти ». Используя его, вы можете найти свой просочившийся объект по типу класса и четко увидеть, кто на него ссылается, выпуская его оттуда, решает вашу проблему. Он также обнаруживает циклы памяти.

Смотрите больше информации о том, как его использовать

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.