Разработка iPhone/iPod touch приложений: Отслеживание утечек памяти

iPhone-бум уверенно распространяется. Все больше девелоперов пробуют свои силы, разрабатывая приложения для iPod и iPhone. И это только начало.

Параллельно популяризация любой платформы как правило идет развитие комьюнити. Девелоперы неизбежно сталкиваются с кучей проблем, и иногда чтение документации не может дать ответы на возникшие вопросы (да, и, если быть откровенным, не все её изучают с достаточным вниманием).

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

Конечно, если проблема новая — то хочешь не хочешь, а решать придется тебе. Читать документацию, копаться в коде, экспериментировать и пр. Ну, и опытом потом делиться — кому-нибудь это обязательно окажется полезным. Хотя многим делиться опытом лень. Думаю, примерно так это все и происходит. Или должно происходить.

Об отслеживании утечек памяти при разработке приложений на Obj-C написано много. Лично я с утечками памяти столкнулся, разрабатывая вторую версию приложения для социальной сети любителей поесть «Crazymenu».

Это был мой первый опыт программирования под Obj-C и поэтому утечки стали для меня неприятным сюрпризом (и не единственным, кстати). Тем более что утечки были замечены практически под конец, когда до дедлайна оставались считанные дни.

В сети мною было найдено несколько очень хороших статей, в которых описывались причины возникновения утечек памяти и инструмент для их обнаружения в xCode. Одна из этих статей была написана в нашей компании (это говорит о том, что мы готовы делиться своим опытом и делаем это, внося свой вклад в общую «базу данных»), и эта статья является ее продолжением.

Данная статья не претендует на уникальность. В ней просто описывается мой опыт решения проблемы, и я надеюсь, что кому-то он окажется полезным.

Причины возникновения утечек памяти

Утечки памяти — это области в оперативной памяти, о которых больше никто не знает. А раз не знает, то и не освобождает, что очень скоро может привести к недостатку оперативной памяти, и ваше приложение просто вылетит.

Такое происходит, когда не соблюдается простое правило: вы и только вы отвечаете за ту память, которую выделили. Это правило актуально не только при программировании под iPhone. Просто при программировании под iPhone это правило становиться крайне актуальным исходя из того, что на iPod/iPhone нет так называемого «сборщика мусора», отвечающего за автоматическое освобождение более не используемых участков памяти.

Соблюдать правило невероятно легко. Для этого, правда, желательно разобраться с тем, как происходит работа с памятью, узнать про счетчики ссылок на объекты и т. п. Об этом написано много, поэтому я не буду останавливаться на этом подробно (если хотите — прочитайте статью «Objective-C работаем с объектами.. и памятью», здесь все описано просто и доступно. И с юмором).

Для работы с памятью у девелопера есть несколько функций:

— alloc (+1)
— copy (+1)
— retain (+1)
— release (-1)
— autorelease (-1)
— dealloc

Dealloc напрямую девелоперу не придется вызывать (единственное место, где в коде вызывается этот метод — это в функции dealloc, где этот метод вызывается для родительского объекта).

Система освобождает объект из памяти, когда на него никто больше не ссылается, т. е. счетчик ссылок на объект равен 0. Первые три метода увеличивают счетчик ссылок на объект на 1, следующие два на 1 его уменьшают.

Иногда следить за этой «арифметикой» не очень удобно, иногда просто лень. Поэтому, чтобы избежать утечек памяти, надо после каждого вызова методов alloc, copy или retain для объекта вызывать метод release или autorelease (если объект надо освободить, но потом). Нужно считать это «правилом хорошего тона» при программировании приложений под iPhone. И вообще: выработайте у себя привычку «прибирать за собой» — это очень полезная привычка (как и привычка писать «красивым» кодом, который легко читать и впоследствии рефакторить, если возникнет необходимость).

Второй неприятный сюрприз

Вторым неприятным сюрпризом… Вернее это был первый сюрприз… Вобщем, неважно. Важно то, что ни в коем случае нельзя считать свое приложение рабочим, если оно работает у вас на симуляторе!

На симуляторе ваше приложение может не вылетать по памяти, все работает быстро и плавно, ничто не тормозит… Ещё бы! Симулятор использует доступную мощность вашего компьютера и поэтому работа приложения не соответствует действительности. Но попробуйте запустить приложение на iPhone… Не обязательно все будет так, как я описываю — может вы куда более аккуратный и опытный программист, чем я. Но в любом случае лучше перестраховаться.

Это крайне неприятный сюрприз! Я был в шоке, когда увидел, как криво работают некоторые вещи в приложении на живом устройстве.

Но есть хорошая новость: как бы неприятно не было смотреть на такую лажу — это явное указание на ошибки в коде. А, зная про ошибки, их можно искать, исправлять, делая тем самым ваше приложение более качественным продуктом. И получать тем самым драгоценный опыт!

Единственный минус — этот сюрприз может поджидать вас, когда релиз не за горами и когда времени и так не хватает.

У меня оно так и получилось.

Как отловить утечки памяти

Тут все просто! Как оказалось =)

Можно смело продолжать петь оду Стиву Джобсу и его команде — инструменты для отслеживания утечек есть, и они хорошо интегрированы в среду разработки xCode. Они так и называется — «Инструменты». И они достаточно удобны, когда разберешься как с ними работать.

Как правило при разработке приложения мы используем режим «отладки». Но если же мы хотим определить, съедает ли наш апп память или нет, нам надо запустить его вместе с соответствующим инструментом, выбрав нужный пункт меню (как показано на скриншоте ниже).

Запуск приложения в режиме ловли утечек памяти

Запуск приложения в режиме ловли утечек памяти

После этого у нас откроется окно Инструментов и, если мы тестируем на симуляторе, откроется симулятор. Для примера я буду использовать симулятор.

Вот так оно выглядит в моем случае

Вот так оно выглядит в моем случае

После этого все, что нам нужно делать — это тестировать наш апп, щелкая по кнопкам, переключаться между табами, вобщем, работать с приложением по его назначению, используя его возможности.

В окне инструментов есть два графика: график выделения объектов (ObjectAlloc) и график, на котором отображаются утечки (это выглядит как оранжевый конус).

Когда начинаешь смотреть как «съедается» память при работе с аппом (что видно на стремительно растущую синюю полоску на первом графике) — становится немного неуютно =) Но это нормально. Главное, чтобы память корректно освобождалась и чтобы делалось это вовремя.

Если в каком-то месте у нас есть утечка памяти — на втором графике появятся конусы. Это и есть наша лажа, которую нам нужно исправить.

А вот и она - утечка...

А вот и она - утечка...

Как определить где у нас утечка? Для этого необходимо щелкнуть на втором графике и внизу, в списке, мы увидим отчет.

В таком виде этим пользоваться не совсем удобно (ИМХО)

В таком виде этим пользоваться не совсем удобно (ИМХО)

Исходя из нашего опыта удобнее всего определять утечки, открыв боковое окно с подробностями и перейдя в «Outline View». Сделать это можно, нажав соответствующие кнопки внизу окна инструментов. На скриншоте ниже я обвел эти две кнопки.

Их надо нажать

Их надо нажать

После этого отчет станет выглядеть вот так:

В таком виде это более понятно (опять ИМХО)

В таком виде это более понятно (опять ИМХО)

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

Словами это описывать не очень удобно. Вероятно то, что я написал выше звучит коряво, но если вы попробуете — вы быстро освоите все нюансы работы с Инструментами.

Ошибка, которую я внес в код, чтобы продемонстрировать работу с отладкой утечек памяти выглядит так:

Простейшая ошибка, но для примера достаточно

Простейшая ошибка, но для примера достаточно

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

Выводы

Наивно полагать, что в программе нет ошибок. Я убедился в этом на своем опыте. Всегда можно найти баг.

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

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

Удачи! И успешных вам приложений!

Комментарии

AppeampWootte

в 5:23, 15.09.2009

почитаем и посмотрим

Тимур

в 12:23, 26.10.2009

Спасибо за статью, я недавно начал програмировать приложения под IPhone, до этого писал на .NET. Если у вас есть еще статьи о практической работе в среде XCode, я бы с радостью с ними ознакомился.

Оставить комментарий