Среди проектов, над которыми мы работаем, пожалуй, нет ни одного, в котором было бы всё в порядке с памятью. Этот пост я посвящу рассказу о том, как бороться с данным видом проблем.
К проблемам с памятью можно отнести утечки и так называемых «зомби». Приложение, в котором творится бардак с памятью, становится нестабильным, в нём появляется целый букет странностей, начинаяя с пропадания некоторых элементов интерфейса и заканчивая падениями.
Для начала давайте определимся, что такое утечки. Утечки — выделенные, но не освобождённые области памяти, на которые нет указателя. Всё это выглядит достаточно несерьёзно, у iPhone куча оперативной памяти, а обычное приложение занимает в лучшем случае 2 Мб. На деле всё не так радужно: приложение может использовать до 3мб, а дальше начинают возникать memory warning, соответственно, ОС пытается освободить те области, которые считает «ненужными». Попросту говоря, удаляет невидимые в данный момент элементы интерфейса, а это не только некрасиво, но и может привести к падению приложения. «Зомби» — уже освобождённые (убитые) области памяти, к которым пытается обратиться программа, что также приводит к падению приложения.
Многие (да я и сам этим грешу) часто говорят, что его нет. Но всё же сборщик мусора есть, хоть и весьма примитивный. Принцип управления памятью основан на подсчёте количества ссылок на объект. При выделении памяти под объект их количество устанавливается равным 1, а при достижении 0 память освобождается (выбрасывается мусор). Каждый раз, когда вы хотите использовать объект, вы должны увеличить количество ссылок, а каждый раз, когда он вам становится ненужным — уменьшить.
Чтобы управлять памятью, нужно запомнить всего 4 слова:
Выделяет память под объект и увеличивает кол-во ссылок на 1. Использовать его необходимо каждый раз, когда вы создаёте новый объект.
Копирует объект (не указатель). Оставляет неизменным количество ссылок на объект, который мы копируем и увеличивает их количество на новый объект.
Просто увеличивает количество ссылок на объект.
Обратная трём вышеперечисленным операция, уменьшает количество ссылок на 1.
Это немного другая история. Если отправить объекту сообщение
Но почему бы тогда не использовать его везде? Это же так удобно! Удобно, но накладно. Из-за того, что количество ссылок на объект уменьшается не сразу, соответственно, память тоже освобождается не сразу. Тут нас и ждёт подводный камень: у нас всего 3мб, а если мы за раз обрабатываем большой объём данных, появляются те самые memory warning.
Этот метод вызывается при удалении объекта, в нём вы должны сделать release всех переменных, которые использовал объект, и в конце вызвать
Свойства бывают трёх видов:
Помните, вам не нужно следить за тем, что делает
В процессе работы мы наткнулись на проблему, связанную с тем, что у нас утекало большое число объектов. Выяснилось, что у нас два объекта ретейнили друг-друга и, соответственно, никогда не освобождали память. Это происходило при использовании паттерна делегирования. Был объект, который создавал другой объект и ставил себя его делегатом. А свойство
Иван Ушаков, программист команды iOS
К проблемам с памятью можно отнести утечки и так называемых «зомби». Приложение, в котором творится бардак с памятью, становится нестабильным, в нём появляется целый букет странностей, начинаяя с пропадания некоторых элементов интерфейса и заканчивая падениями.
Для начала давайте определимся, что такое утечки. Утечки — выделенные, но не освобождённые области памяти, на которые нет указателя. Всё это выглядит достаточно несерьёзно, у iPhone куча оперативной памяти, а обычное приложение занимает в лучшем случае 2 Мб. На деле всё не так радужно: приложение может использовать до 3мб, а дальше начинают возникать memory warning, соответственно, ОС пытается освободить те области, которые считает «ненужными». Попросту говоря, удаляет невидимые в данный момент элементы интерфейса, а это не только некрасиво, но и может привести к падению приложения. «Зомби» — уже освобождённые (убитые) области памяти, к которым пытается обратиться программа, что также приводит к падению приложения.
В iOS есть сборщик мусора!
Многие (да я и сам этим грешу) часто говорят, что его нет. Но всё же сборщик мусора есть, хоть и весьма примитивный. Принцип управления памятью основан на подсчёте количества ссылок на объект. При выделении памяти под объект их количество устанавливается равным 1, а при достижении 0 память освобождается (выбрасывается мусор). Каждый раз, когда вы хотите использовать объект, вы должны увеличить количество ссылок, а каждый раз, когда он вам становится ненужным — уменьшить.
Есть 4 волшебных слова
Чтобы управлять памятью, нужно запомнить всего 4 слова:
alloc
, copy
, retain
, release
. Давайте поговорим о каждом из них в отдельности: alloc
Выделяет память под объект и увеличивает кол-во ссылок на 1. Использовать его необходимо каждый раз, когда вы создаёте новый объект.
copy
Копирует объект (не указатель). Оставляет неизменным количество ссылок на объект, который мы копируем и увеличивает их количество на новый объект.
retain
Просто увеличивает количество ссылок на объект.
release
Обратная трём вышеперечисленным операция, уменьшает количество ссылок на 1.
И еще кое что
Autorelease
Это немного другая история. Если отправить объекту сообщение
autorelease
, то произойдёт «отложенный» release
. Это нужно для тех случаев, когда вы не сможете по каким либо причинам отправить сообщение release
объекту после действий с ним. Его основное применение — возвращение объектов методами. Любой метод, в названии которого нет alloc
, copy
, retain
, возвращает и должен возвращать «авторелизный» объект. Но почему бы тогда не использовать его везде? Это же так удобно! Удобно, но накладно. Из-за того, что количество ссылок на объект уменьшается не сразу, соответственно, память тоже освобождается не сразу. Тут нас и ждёт подводный камень: у нас всего 3мб, а если мы за раз обрабатываем большой объём данных, появляются те самые memory warning.
Dealloc
Этот метод вызывается при удалении объекта, в нём вы должны сделать release всех переменных, которые использовал объект, и в конце вызвать
dealloc
у родителя.- (void)dealloc {
[model release];
[view release];
[super dealloc];
}
Property
Свойства бывают трёх видов:
assign
, retain
, copy
. Последние два должны быть уже понятны. А первый просто присваивает значение, никак не влияя на количество ссылок на объект. Приведу правильную реализацию сеттеров для каждого типа: //assign
- (void)setList:(List *)list{
_list = list;
}
//retain
- (void)setList:(List *)list{
[_list autorelease];
_list = [list retain];
}
//copy
- (void)setList:(List *)list{
[_list autorelease];
_list = [list copy];
}
Помните, вам не нужно следить за тем, что делает
Property
, вам достаточно уcтановить его, а при необходимости сбросить значение, просто обнулить его. Delegate, курица и яйцо
В процессе работы мы наткнулись на проблему, связанную с тем, что у нас утекало большое число объектов. Выяснилось, что у нас два объекта ретейнили друг-друга и, соответственно, никогда не освобождали память. Это происходило при использовании паттерна делегирования. Был объект, который создавал другой объект и ставил себя его делегатом. А свойство
Delegate
было прописанно как retain
. Ситуацию спасает замена retain
на assign
. Несколько советов напоследок
- Слово release должно приходить сразу после
alloc
,retain
илиcopy
- А слово nil сразу после
release
- Всегда обnilяйте делегатов
- По возможности воздержитесь от
autorelease
- Создавайте объекты именно тогда, когда они вам нужны
- Не злоупотребляйте полями класса
- Static Analysys Tool всё красиво и понятно рисует
- Изредка проверяйте на утечки
Иван Ушаков, программист команды iOS