Использование памяти CALayer при рисовании - неявный кеш? - PullRequest
5 голосов
/ 01 октября 2010

У меня странная проблема с CoreGraphics / CoreAnimation на iPhone.Чтобы лучше объяснить, как проявляется проблема, я проведу вас через мои текущие настройки и проиллюстрирую, где это уместно, кодом.

Я пытаюсь нарисовать кучу предварительно загруженных изображений в UIView 's CALayer, но всякий раз, когда изображение отображается, пики в памяти приложения возрастают, а память не восстанавливается при каждом изменении изображения.

Предварительная загрузка изображений выполняется путем чтения их с помощью средств UIImage и их рендеринга.на растровый контекст и извлечение CGImageRef из этого контекста.Цель этого состоит в том, чтобы распаковать и масштабировать изображения, чтобы эти операции не выполнялись при каждом рисовании.Аналогичная рекомендация может быть найдена в вопросах и ответах Apple по этому вопросу (ищите производительность CGContextDrawImage, если вам интересно).Контекст устанавливается с 8 битами на компонент и предварительно умноженным альфа.

После того, как изображения распакованы в растровое изображение, они сохраняются в NSArray и позже назначаются (не сохраняются) для пользовательского UIView подкласс, который делает рисунок.Я пробовал разные подходы к рисованию изображений, и самым быстрым способом на данный момент является непосредственная установка свойства вида CALayer contents.Другие методы, такие как drawLayer:inContext: и drawRect:, оказывают различное влияние на частоту кадров, но все они демонстрируют одинаковое поведение памяти.

Проблема заключается в том, что ... после изменения свойства contents я вижу всплескв памяти инструментов, и эта память не уменьшается даже после того, как изображение больше не отображается.Распределение объектов остается постоянным, поэтому я предполагаю, что CoreAnimation создает неявный кэш для ускорения рисования.Однако, как я уже сказал, этот кэш не освобождается, когда это необходимо, и постепенное наращивание приводит к сбою после нескольких минут работы.

Свойство contents сохраняет объект, и я не могуявно не выпустить его, потому что я хочу, чтобы исходные изображения оставались в памяти на время выполнения приложения;Кроме того, большое количество сохраняемых данных не учитывает всплески памяти, которые я вижу.

При проверке стека я вижу, что CoreAnimation вызывает такие функции, как CA::Render::copy_image, что приводит меняполагать, что это дублирует содержимое слоя где-то вне досягаемости.Я полагаю, что для этого есть веская причина, но незнание того, как и когда это исправить, в настоящее время является ошибкой, препятствующей показу.

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

Спасибо.

Ответы [ 3 ]

5 голосов
/ 05 октября 2010

@ CouchDeveloper - спасибо, что нашли время ответить.

Я еще немного покопался, и я отвечу на свой вопрос вместо того, чтобы отвечать прямо, возможно, понимание, которое я получил 'Полученные результаты помогут другим людям, имеющим аналогичные проблемы.

Ваши наблюдения о распределении и использовании памяти верны.Память, на которую я смотрю, - это «реальная» (резидентная) память, используемая приложением в инструменте «Монитор памяти» (Activity Monitor).

Мне кажется, я обнаружил причину этих таинственных распределений памяти.Когда CoreAnimation встречает изображение не в собственном пиксельном формате, оно копирует изображение в свой собственный буфер, и это, похоже, использование, которое я вижу.Я считаю, что правильный формат - 32-битный, порядок байтов хоста, предварительно умноженный альфа-канал (растровые информационные флаги kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host).

Для того, чтобы узнать, какие экземпляры CGImageRef копируются, и получить более широкий взгляд на внутреннюю часть инфраструктурычто вы можете использовать инструмент Core Animation на устройстве или указать несколько переменных среды и запустить iPhone Simulator из командной строки.Список всех флагов можно найти здесь .В моем случае, наиболее полезная комбинация - CA_COLOR_COPY и CA_LOG_IMAGE_COPIES, которая даст скопированному изображению голубой оверлей и будет записывать копии в stderr.

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

Надеюсь, этоможет быть полезным для тех, кто интересуется, откуда у них проблемы с памятью и производительностью!

2 голосов
/ 04 октября 2010

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

Однако я проверяю, что CA распределяет системную память, которая записывается не в «Распределение объектов» прибора, а в «Монитор активности», соответственно, в «Монитор памяти» прибора, а именно «Свободная физическая память». «Свободная физическая память» уменьшается, когда изображение отрисовывается в неэкранном контексте, а также уменьшается, когда отображается это изображение.

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

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

Если вы не предоставите свой источник, я бы заподозрил, что ваш счет сохранения ваших CGImageRefs не сбалансирован и, следовательно, они протекают. Обратите внимание, что CGImageRef не следует помещать в NSArray напрямую - они не ведут себя как объекты Objective-C (иначе говоря, бесплатный мост к UIImage) - вам нужно явно вызвать CGImageRetain / CGImageRelease.

1 голос
/ 17 декабря 2010

Нашли ли вы решение вашей проблемы? У меня точно такая же проблема. Я использую 32-битные файлы PNG, я подумал, что этого будет достаточно. Я удостоверился, что я не использую ImageNamed, поскольку этот известен тем, что кэширует вещи.

Я использовал imageWithData и даже удостоверился, что мой NSData initWithContentsOfFile имеет опцию «nocache» NSRead, чтобы предотвратить кэширование FS, но это ничего не изменило.

ОБНОВЛЕНИЕ: я нашел, в чем была моя проблема. не удалил представление из его суперпредставления (которое не отображалось), и, похоже, где-то сохранилось некоторое количество кэша ...

...