2015-03-13 3 views
1

Я работаю над программой MacOS в Objective C, которая должна создавать миниатюры в памяти для отправки на сервер. Для выполнения этой операции используется следующий код. По мере запуска программы каждый раз, когда вызывается этот метод, возникает утечка около 40 МБ. Я подозреваю, что у меня нет чего-то действительно основного, но я не вижу источника проблемы.Создание эскизов вызывает утечку (MacOS, Obj C)

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

Когда я запускаю Инструменты, распределения для категории «VM: ImageIO_JPEG_Data» растут по одному распределению 40 МБ каждый раз при его вызове. Ответственной библиотекой является «ImageIO», а ответственным абонентом является «ImageIO_Malloc».

- (void) createPhotoThumbnail 
{ 
    NSURL* fileURL = [NSURL fileURLWithPath : _imagePath]; 

    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef bitmapContext = CGBitmapContextCreate(NULL, MAX_THUMB_DIM, MAX_THUMB_DIM, 8, 0, 
                 colorspace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast); 
    CIContext *ciContext = [CIContext contextWithCGContext: bitmapContext options: @{}]; 

    if (fileURL) 
    { 
     CIImage *image = [[CIImage alloc] initWithContentsOfURL: fileURL]; 

     if (image) 
     { 
      // scale the image 
      CIFilter *scaleFilter = [CIFilter filterWithName: @"CILanczosScaleTransform"]; 
      [scaleFilter setValue: image forKey: @"inputImage"]; 
      NSNumber *scaleFactor = [[NSNumber alloc] initWithFloat: ((float) MAX_THUMB_DIM)/
            ((float)MAX(_processedWidth, _processedHeight))]; 
      [scaleFilter setValue: scaleFactor forKey: @"inputScale"]; 
      [scaleFilter setValue: @1.0 forKey: @"inputAspectRatio"]; 
      CIImage *scaledImage = [scaleFilter valueForKey: @"outputImage"]; 

      NSMutableData* thumbJpegData = [[NSMutableData alloc] init]; 
      CGImageDestinationRef dest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)thumbJpegData, 
                      (__bridge CFStringRef)@"public.jpeg", 
                      1, 
                      NULL); 
      if (dest) 
      { 
       CGImageRef img = [ciContext createCGImage:scaledImage 
                fromRect:[scaledImage extent]]; 
       CGImageDestinationAddImage(dest, img, nil); 
       if (CGImageDestinationFinalize(dest)) 
       { 
        // encode it as a string for later 
        _thumbnail = [thumbJpegData base64EncodedStringWithOptions: 0]; 
       } 
       else 
       { 
        DDLogError(@"Failed to generate photo thumbnail"); 
       } 
       CGImageRelease(img); 
       CFRelease(dest); 
      } 
      else 
      { 
       DDLogError(@"Failed to finalize photo thumbnail image"); 
      } 
      thumbJpegData = nil; 
     } 
    } 

    CGContextRelease(bitmapContext); 
    CGColorSpaceRelease(colorspace); 
    ciContext = nil; 

} 

UPDATE: Я включил код использовать CGAffineTransform вместо фильтра с «CILanczosScaleTransform» и этот симптом не изменился. Затем я использовал совершенно новый метод (фрагмент ниже), но проблема остается.

NSImage *thumbnail = [[NSImage alloc] initWithSize: newSize]; 

[thumbnail lockFocus]; 
[sourceImage setSize: newSize]; 
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; 
[sourceImage compositeToPoint: NSZeroPoint operation: NSCompositeCopy]; 
[thumbnail unlockFocus]; 

NSData *tiff = [thumbnail TIFFRepresentation]; 
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData: tiff]; 
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor]; 
NSData *thumbJpegData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps]; 

Это заставляет меня думать, что проблема, возможно, связана с чем-то присущим образом я делаю это. Мне трудно поверить, что два разных метода масштабирования изображений будут демонстрировать такую ​​же утечку.

+0

Насколько велики ваши входные изображения, которые вы масштабируете? Вы используете ARC? – Sven

+1

@Sven - Несмотря на самоограничивающиеся замечания OP, это не обычное «простое несоблюдение« Create Rule », и это не вопрос ARC. Я могу воспроизвести поведение, описанное OP, используя ARC, и я не вижу здесь ничего явно неправильного. Для меня это утечка несжатого размера изображения (поэтому мой снимок экрана 2888x1800 пропускает 20 мб для каждого изображения). Использование иваров здесь немного неряшливо, но даже после того, как один из них исправит, фундаментальная проблема утечки памяти выглядит законной для меня. – Rob

+0

Да, используя ARC. Мои изображения - это разные фотографии, поэтому размер варьируется довольно немного (от более старой 3-мегапиксельной камеры до более новой 12-мегапиксельной камеры), но размер кажется согласованным на уровне около 40 МБ на изображение. –

ответ

0

Проблема не в CGImageDestinationRef логики, потому что до сих пор просачивается даже если заменить что-то далеко просто, например:

NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCIImage:scaledImage]; 
NSData *data = [rep representationUsingType:NSJPEGFileType properties:nil]; 

Копания немного дальше, то может показаться, что проблема, как представляется, быть проблемой в пределах CILanczosScaleTransform. Если вы используете inputScale@1.0, утечка исчезнет. Но используйте что-то менее @1.0 (даже @0.5), и оно течет.

Я предлагаю вам рассмотреть возможность поиска другого способа изменения размера изображения.

+0

Есть ли у вас конкретная альтернативная рекомендация по изменению размера. У меня есть две потребности: одна - создавать эскизы определенного максимального размера независимо от размера исходного изображения.Другой способ состоит в том, чтобы извлекать миниатюры фиксированного размера из определенных мест на исходной фотографии (обрезать сначала, другими словами), и в этом случае масштабирование может быть масштабированием по вертикали или вниз. Спасибо, что сузили его до трансформации. –

+0

Нет, я парень из iOS, поэтому я, вероятно, не тот человек, которого можно спросить. Но поиск Stack Overflow для «NSImage resize» придумал несколько хитов, таких как [this] (http://stackoverflow.com/questions/11949250/how-to-resize-nsimage), [this] (http: // stackoverflow.com/questions/5264993/resize-and-save-nsimage) или [это] (http://stackoverflow.com/questions/2531812/trying-to-resize-an-nsimage-which-turns-into-nsdata). Я уверен, что «NSImage crop» даст эквивалентные ссылки. Но мой опыт работы с CIFilter заключается в том, что он мощный и элегантный, но, тем не менее, медленнее, поэтому найти альтернативы, вероятно, разумно. – Rob

+0

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

0

Благодаря this answer Мне удалось определить необходимость в авторекладе, о чем я совершенно не подозревал. Код в вопросе является одним из ряда методов, которые многократно вызываются изнутри замкнутого цикла. Это, по-видимому, мешает ОС иметь возможность сделать некоторую очистку. Блок теперь выглядит следующим образом:

@autoreleasepool { 
    [self findRelevantAdjustments]; 
    [self adjustForStraightenCrop]; 
    [self moveFacesRelativeToTopLeftOrigin]; 
    [self createPhotoThumbnail]; 
    [self sendPhotoToServer]; 
} 

Мораль: даже с ARC есть больше вещей, чтобы обратить внимание, когда речь идет о жизненном цикле памяти.