Сохранена память NSImage drawInRect и NSView cacheDisplayInRect
Приложение, которое я работаю над обработкой изображений. Это как пользователь падает на максимум 4 изображения и размещает их в приложении на основе выбранного пользователем шаблона. Одно изображение может быть добавлено 2-3 раза в конечном виде.
Каждое изображение в макете рисуется в NSView (метод drawRect NSView с использованием метода drawInRect). Теперь конечное изображение (объединенное изображение путем размещения всех изображений) создается путем сохранения NSView как изображения, и все это работает очень хорошо.
Теперь проблема, с которой я сталкиваюсь, заключается в том, что приложение сохраняет память после завершения всей обработки. Я использовал распределение инструментов и не вижу утечек памяти, но вижу, что "постоянные байты" постоянно увеличиваются с каждым сеансом приложения, и один пользователь сообщил о проблеме в ГБ. Пожалуйста, смотрите скриншот.
Когда я продолжил исследования в 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);
Надеюсь, это поможет кому-то.