Построение Memory-эффективных приложений

П

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

Что такое memory-эффективное приложение?

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

Начнем с истории.

ARC — Автоматический подсчет ссылок

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

Таким образом, вы могли писать memory-эффективные приложения, потому что объекты немедленно освобождались(проще, уничтожались), когда они были больше не нужны. Но с другой стороны, ручное управление памятью было очень трудоемким. И очень часто возникали трудности с поиском ошибок. Стало ясно, что ручное управление — это не лучшее решение проблемы памяти.

Решение было введено с iOS 5: Автоматический подсчет ссылок (ARC). Теперь команды подсчета ссылок производились во время компиляции. С одной стороны, это по-прежнему способствовало memory-эффективному коду, а с другой стороны, разработчику не нужно больше работать с памятью вручную. Решение было настолько классным, что в то же время все приложения Mac OS X так же стали использовать ARC.

Тем не менее, даже если вам не нужно больше считать ссылки самостоятельно, на вас все еще лежит большая ответственность.

Выбор правильного Deployment Target

Deployment Target — это минимальная версия iOS, на которой будет способно запуститься ваше приложение. Из этого следуют разные ситуации. Например, если вы хотите поддерживать iOS 5 и выше, то это значит, что вы будете поддерживать и iPad первого поколения, у которого всего 256 мегабайт оперативной памяти. Конечно, чем шире поддержка, тем лучше. Но было бы неплохо при этом обеспечить комфортное использования приложения на всех устройствах, которые поддерживаются. Если вы хотите поддерживать старые устройства, то на этапе проектирования вы должны принять это во внимание. Вот краткий список самых старых поддерживаемых устройств для iOS 5-9:

  • iOS 9 — iPhone 4S / iPad 2 / iPad Mini 1
  • iOS 8 — iPhone 4S / iPad 2 / iPad Mini 1
  •  iOS 7 — iPhone 4 / iPad 2 / iPad Mini 1
  • iOS 6 — iPhone 3GS / iPad 2 / iPad Mini 1
  • iOS 5 — iPhone 3GS / iPad 1

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

Изображения

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

  • Во-первых, изображения должны быть только такого размера, какой необходим! Если у вас есть представление таблицы, у которого есть UIImage 100 х 100 пикселей, то будет очень плохой идеей использовать изображения с 1000 × 1000 пикселей. Падение производительности будет очень высоким! Если вы скачиваете изображения с сервера, то это должна быть забота сервера — предоставить вам изображения в нужном размере.
  • Во-вторых, убедитесь, что в приложении столько изображений, сколько нужно! Например, у вас есть приложение-галерея на 5000 изображений. Было бы глупо загружать все и сразу. Умным шагом было бы держать в памяти только то, что есть на экране. UITableView так и работает — по умолчанию держит в памяти только то, что показано на экране. От этого, имея в таблице 5000 ячеек, по факту в памяти у нас всегда их 8-10. Это частный случай так называемой ленивой загрузки.

Ленивая загрузка

Идея ленивой загрузки подразумевает загрузку ресурсов как можно позже. Это имеет два преимущества:

  • Время загрузки лучше распределено
  • Существует возможность полностью избежать загрузки ресурса.

Так как это делается в разработке под IOS? Как упоминалось ранее, представления таблицы являются хорошим примером ленивой загрузки. Еще один хороший путь заключается в использовании ключевого слова lazy для свойств. Представьте что вы хотите использовать широкий спектр продуктов, которые используется при определенном взаимодействии с пользователем во ViewController:

Этот массив загружается всегда — даже если в нем нет необходимости. Это пустая трата памяти. Но если вы используете ключевое слово lazy, то массив инициализируется только тогда, когда кто то попробует получить к нему доступ:

Даже если это небольшие массивы, регулярное использование lazy поможет сохранить много памяти.

ViewController-ы и retain-циклы

Одна из худших вещей, которые могут произойти с точки зрения проблем памяти это то, что ViewController не высвобождается из нее, даже если он уже не нужен. Самой распространенной причиной для такого сценария является так называемый retain-цикл (Обнаружение retain-циклов). Представьте: у вас есть ViewController A, который инициализирует ViewController B в качестве своего child (внутри себя). Потом VC A дает ссылку на себя же в VC B. Теперь они имеют сильные (strong) ссылки друг на друга.

Теперь, если ViewController убрать, закрыть и т.д., то оба ViewController-а не высвободятся из памяти, потому что оба они имеют сильную ссылку друг на друга. Вы можете избежать этого, используя ключевое слово weak. Например, если ViewController A устанавливает себя в качестве делегата для ViewController B, соответствующее свойство может быть объявлено в VC B следующим образом:

Хороший способ проверить, все ли высвобождается правильно — проверять контроллер с помощью лог-сообщения в методе deinit соответствующего контроллера:

Теперь, если все работает правильно, при закрытии определенного контроллера сработает его метод deinit и выведет в лог соответствующее сообщение.

Контроль использования памяти

Не редкость, что плохой контроль памяти обнаруживается в конце работы над проектом. К сожалению, уже слишком поздно. Так что очень важно контролировать использование памяти регулярно. Что бы сделать это, просто запустите приложение на устройстве и нажмите в Xcode Debug Navigator во вкладку «Memory».

memory

Управление памятью — важная тема в разработке мобильных приложений. Если ваше приложение использует слишком много памяти, то оно становится медленным, и появляется большая вероятность того, что система его «обвалит» (да-да, система сама крашит слишком прожорливые приложения).

Поддержите ресурс blog.justDev:

Сведения об авторе

Игорь Малеваный

Добавить комментарий

Instagram

Поддержите ресурс blog.justDev:

Свежие записи

Рубрики