tg-me.com/iosdev/537
Last Update:
Почему ваша картинка размером в 2 мегабайта может отъедать 80 мб памяти? Сейчас разберёмся.
Или как пофиксить одну из причин out of memory exception
На собеседованиях часто просят отобразить ленту из картинок, взятых откуда-нибудь из стороннего ресурса, гугла, например, условного unsplash или откуда-нибудь ещё. Скорее всего, и у инженеров на боевых проектах рано или поздно может возникнуть подобная задача.
Одна из потенциальных сложностей, с которой можно столкнуться при такой задаче — out of memory exception
. Так в чём же дело?
Использование памяти нашим приложением резко возрастает, когда мы начинаем показывать HD-изображения на экране (даже одно уже может внести существенный импакт для увеличения используемой памяти).
Главное, что нужно запомнить, использование памяти ≠ размеру файла. И вот почему.
Использование памяти связано с размерами изображения, а не с размером файла (Session 416, WWDC 2018).
Всё дело в том, что для отображения изображения на экране, iOS сначала необходимо декодировать и распаковать изображение. Обычно 1 пиксель декодированного изображения занимает 4 байта памяти — 1 байт для красного, 1 байт для зеленого, 1 байт для синего и 1 байт для альфа-канала (да-да, тот самый RGB с альфой). Например:(3648 * 5472) * 4 bytes ≈ 80MB
И именно это значение и будет отображаться, когда мы будем чекать, что же происходит в приложении.
Что со всем этим делать?
Разработчики в этой статье пробовали менять масштаб и перерисовать изображение, но это не помогло. Это связано с тем, что операции для изменения размера для UIImage
дороги. В процессе изменения размера iOS по-прежнему будет декодировать и распаковывать исходное изображение, вызывая нежелательный скачок памяти.
Одно из решений, которым делятся инженеры Apple — юзать даунсэмплинг.
Мы можем использовать ImageIO
для изменения размера изображения перед его отображением на экране. По факту, для нас это выльется только в стоимости ресайзинга исходной картинки. Вот здесь можно чекнуть сниппет.
Флаги, которые можно использовать
1️⃣ kCGImageSourceShouldCache
— когда для этого флага установлено значение false
, мы сообщаем основному фреймворку, что нам нужно только создать ссылку на источник изображения и не хотим декодировать изображение сразу при создании объекта CGImageSource
.
В ситуации, когда у вас нет доступа к пути к источнику изображения, вы можете создать объект CGImageSource, используя инициализатор CGImageSourceCreateWithData().
2️⃣ kCGImageSourceShouldCacheImmediately
— этот флаг указывает, что декодировать изображение нужно именно в тот момент, когда мы запускаем процесс понижения дискретизации.
3️⃣ kCGImageSourceCreateThumbnailWithTransform
— установка этого флага в значение true
очень важно для сохранения исходной ориентации.
Подводные камни (а как же без них)
Имейте в виду, что даунсэмплинг — это процесс, который потребляет ресурсы процессора. Таким образом, по-прежнему предпочтительнее использовать правильно масштабированный источник изображения, а не понижать дискретизацию HD-изображения. Другими словами, вы должны использовать даунсэмплинг только тогда, когда вам нужно отобразить изображение, размер которого намного превышает требуемый размер на экране.
Важные моменты
⚪ Помните, что память — это конечный и общий ресурс.
⚪ Используйте встроенный в Xcode мониторинг.
⚪ Позвольте iOS выбрать ваши форматы изображений, когда это возможно.
⚪ Используйте ImageIO для понижения разрешения изображений (помните про подводные камни).
⚪ Выгружайте крупные ресурсы, которые находятся за пределами экрана.
⚪ Не игнорируйте графики памяти (memory graphs), чтобы лучше понять, что происходит.
Что почитать ещё?
📖 Deep Dive into iOS Memory.
📖 Reducing Memory Footprint When Using UIImage.
📺 Session 416, WWDC 2018.
🔗 https://developer.apple.com/documentation/imageio/cgimagesource.
@iOS Dev — отвечаем на вопросы.
BY iOS Dev

Share with your friend now:
tg-me.com/iosdev/537