Сохранена память NSImage drawInRect и NSView cacheDisplayInRect

Приложение, которое я работаю над обработкой изображений. Это как пользователь падает на максимум 4 изображения и размещает их в приложении на основе выбранного пользователем шаблона. Одно изображение может быть добавлено 2-3 раза в конечном виде.

Каждое изображение в макете рисуется в NSView (метод drawRect NSView с использованием метода drawInRect). Теперь конечное изображение (объединенное изображение путем размещения всех изображений) создается путем сохранения NSView как изображения, и все это работает очень хорошо.

Теперь проблема, с которой я сталкиваюсь, заключается в том, что приложение сохраняет память после завершения всей обработки. Я использовал распределение инструментов и не вижу утечек памяти, но вижу, что "постоянные байты" постоянно увеличиваются с каждым сеансом приложения, и один пользователь сообщил о проблеме в ГБ. Пожалуйста, смотрите скриншот.

Использование памяти инструментовImageIO_TIFF Когда я продолжил исследования в Instruments, я увидел ниже снимки кода приложения, которые вызывают удержание памяти. Все они связаны с ImageIO и coreImages. Смотрите ниже из инструментов:

Код инструмента привязки

Однако это, похоже, единственная проблема с системой 10.10 и выше. Протестирована та же версия приложения в 10.9.x и использование памяти остается в 60MB. Во время выполнения сеанса в приложении оно достигает 200 МБ, но после завершения возвращается к 50-60 МБ, что обычно для такого приложения.

[_photoImage drawInRect: self.bounds fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0 respectFlipped: YES hints: nil];
            _photoImage = nil;

Выше кода, который я использую для рисования изображения в методе drawRect NSView, и код, показанный на изображении, используется для получения NSView как изображения.

Обновление: после моего дальнейшего расследования я обнаружил, что это CGImageSourceCreateWithData, который кэширует данные TIFF NSImage. Более того, я использую код ниже, чтобы обрезать изображение, и если я раскомментирую его, потребление памяти просто отлично работает.

NSData *imgData = [imageToCrop TIFFRepresentation];
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);
CGImageRef maskRef =  CGImageSourceCreateImageAtIndex(source, 0, NULL);
CGImageRef imageRef = CGImageCreateWithImageInRect(maskRef, rect);
NSImage *cropped = [[NSImage alloc] initWithCGImage: imageRef size:rect.size];

CGImageRelease(maskRef);
CGImageRelease(imageRef);
CFRelease(source);
//CFRelease( options );
imgData = nil;

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

1 ответ

Решение

Наконец, после многих отладок выясняется, что CGImageSourceCreateWithData где-то хранит данные TIFF NSImage. Когда я изменил эту строку:

CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);

с

CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path], NULL);

все только начало работать нормально, и использование памяти приложением было уменьшено с 300 МБ (для 6 изображений) до 50-60 МБ, и теперь это стабильно.

Помимо вышеперечисленных изменений, это по-прежнему вызывало сохранение памяти где-то, поэтому, чтобы избавиться от этого, после всей обработки я очистил изображение каждого слоя до нуля, и это работает как шарм. У меня сложилось впечатление, что создание parent как 'nil' также позволит выпустить изображения, но это не сработало.

В любом случае, если кому-то кажется, что проблема с drawInRect или cacheDisplayInRect, то обязательно удалите изображение, если оно не понадобится позже.

Обновление 2 июля 2016

Я обнаружил, что kCGImageSourceShouldCache имеет значение false по умолчанию в 32-битной версии и true для 64-битной. Я смог освободить память с помощью приведенного ниже кода, установив для него значение false.

const void *keys[] =   { kCGImageSourceShouldCache};
    const void *values[] = { kCFBooleanFalse};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)[image TIFFRepresentation], optionsDictionary);

Надеюсь, это поможет кому-то.

Другие вопросы по тегам