Почему UIImageView так много памяти по сравнению с CGContextDrawImage - PullRequest
5 голосов
/ 10 февраля 2011

При разработке iPad PDF-Reader мы решили подготовить изображения с высоким разрешением для рендеринга интенсивных страниц (в них много путей) и использовать их вместо страниц PDF, чтобы избежать проблем с производительностью. Мы решили, что 3*768 by 3*1024 - это хороший компромисс между удобочитаемостью и производительностью рендеринга, что приводит к ~ 1,5 МБ JPEG .

Однако мы протестировали две реализации для отображения страниц с изображениями. Один использует подкласс CATiledLayer, который также отвечает за обработку "обычных" страниц PDF (рисование с CGContextDrawImage), а другой использует UIImageView. Преимущество последнего заключается в том, что отображение и масштабирование выполняются очень быстро, но использование памяти действительно плохо - это занимает около 30 МБ в памяти (что соответствует размеру растрового изображения). Другой подход (CATiledLayer) требует больше времени для первого отображения страницы и еще две секунды для повторного рендеринга после масштабирования (аналогично PDF-страницам, но гораздо быстрее), но не занимает больше памяти, чем необходимо для отображения. намного меньшее изображение или страница PDF.

Кто-нибудь знает, что происходит за кулисами, и возможно ли совместить низкое использование памяти CGContextDrawImage с высокой производительностью UIImageView с помощью Quartz Framework.

1 Ответ

4 голосов
/ 04 июля 2011

Не уверен, что этот вопрос все еще актуален, но если это так, возможно, это поможет:

Я боролся за эффективное отображение больших изображений в моем мозаичном представлении . Проблема состоит из нескольких основных частей:

  • Обычный UIView, включая UIImageView, кажется, всегда полностью поддерживается памятью для своего изображения. Даже если вы реализуете метод drawRect:, кажется, что он всегда проходит все границы представления, а не только область, видимую в представлении с прокруткой. Как вы обнаружили, это быстро занимает много памяти, поскольку каждый пиксель занимает 4 байта.
  • CATiledLayer запрашивает только содержимое для видимых плиток. Он также отбрасывает тайлы, которые больше не видны - отсюда и экономия памяти. Тем не менее, он делает уведомления и рисование в фоновом потоке, в то время как анимация от белого к содержимому. Похоже, что это делается через частный API, я не нашел способа повторно реализовать функциональность CATiledLayer в качестве моего собственного подкласса CALayer, так как просто не существует механизма уведомления, который мы могли бы использовать как простые смертные.
  • Если у вас есть несколько представлений в представлении прокрутки, они получают drawRect: сообщений соответствующим образом, поскольку они перемещаются по страницам. UIKit, похоже, борется со слишком большим количеством подвидов ниже представления.

Для вас я вижу несколько возможных вариантов:

  • Может быть возможно спасти реализацию на основе CATiledLayer. fadeDuration по умолчанию составляет 0,25 секунды, что может быть слишком длинным, если время загрузки короткое. Вы можете опустить это прямо на что-то вроде 1.0/60.0, то есть один кадр. Из вашего описания неясно, покрывают ли ваши изображения весь размер страницы или только каждый элемент размером 256x256 пикселей. Декодирование всего JPEG снова и снова для каждой плитки будет намного медленнее, чем декодирование отдельных файлов плиток.
  • Если задержка от выполнения всего из потока CATiledLayer слишком высока, вы можете вручную создать связку плиток в виде UIImageView подпредставлений для пустого UIView, который является основным подпредставлением для представления прокрутки. Каждому из этих подпредставлений назначается свое собственное изображение плитки. Если вам повезет, UIKit будет достаточно умен, чтобы отбросить содержимое этих представлений и повторно загрузить соответствующие JPEG-файлы по требованию.
  • Если UIImageViews недостаточно умны или у вас слишком много представлений для UIKit, чтобы справиться с ними, вы можете создать свои собственные представления, загружающие их JPEG в drawRect:. Если он все еще слишком прерывистый, попробуйте использовать собственные CALayers, которые будут подслоями пустого слоя вашего отдельного представления, и снова загрузите их изображение по требованию. Это то, с чем я в конечном итоге согласился на просмотр мозаичного изображения.

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

...