Обложка канала

iOS Dev

iOS разработчик, который делится фишками, своим опытом и опытом других. В этом канале вы сможете найти истории из жизни, подходы к реализации а также новости и тренды из мира iOS-разработки Авторский канал, iOS разработка

iOS Dev

4 года назад
Открыть в
Почему ваша картинка размером в 2 мегабайта может отъедать 80 мб памяти? Сейчас разберёмся. Или как пофиксить одну из причин out of memory exception На собеседованиях часто просят отобразить ленту из картинок, взятых откуда-нибудь из стороннего ресурса, гугла, например, условного unsplash или откуда-нибудь ещё. Скорее всего, и у инженеров на боевых проектах рано или поздно может возникнуть подобная задача. Одна из потенциальных сложностей, с которой можно столкнуться при такой задаче — out of memory exception. Так в чём же дело? Использование памяти нашим приложением резко возрастает, когда мы начинаем показывать HD-изображения на экране (даже одно уже может внести существенный импакт для увеличения используемой памяти). Главное, что нужно запомнить, использование памяти ≠ размеру файла. И вот почему. Использование памяти связано с размерами изображения, а не с размером файла (Session 416, WWDC 2018). Всё дело в том, что для отображения изображения на экране, iOS сначала необходимо декодировать и распаковать изображение. Обычно 1 пиксель декодированного изображения занимает 4 байта памяти — 1 байт для красного, 1 байт для зеленого, 1 байт для синего и 1 байт для альфа-канала (да-да, тот самый SRGB). Например: (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. 🔗 developer.apple.com/documen…gesource. @iOS Dev — отвечаем на вопросы.