title: Memory leak when resizing images using CoreGraphics on background thread
body: <p>I'm building an app that uploads resized versions of many images to the server, and I seem to have a memory leak that I can't track down.  Here's my method that creates the multi-part request (using Restkit 0.2):</p>
  
  <pre><code>[self.objectManager
       multipartFormRequestWithObject:nil
       method:RKRequestMethodPUT
       path:pathString
       parameters:@{@"photo": photoParamater}
       constructingBodyWithBlock:^(id&lt;AFMultipartFormData&gt; formData) {
         @autoreleasepool {
           if (uploadImage) {
             NSData *imageData =
             [photo scaledImageDataWithSmallerDimension:IMAGE_UPLOAD_SMALLER_DIMENSION
                                     compressionQuality:IMAGE_UPLOAD_JPEG_QUALITY];
             imageDataBytes += imageData.length;
             [formData appendPartWithFileData:imageData
                                         name:photo.file_key.absoluteString
                                     fileName:photo.filename
                                     mimeType:@"image/jpg"];
           }
         }
       }];
  </code></pre>
  
  <p>This is part of a method that is called from the main() function of a NSOperation on a background queue.</p>
  
  <p>Here's are the photo methods that get me the actual photo data.  The meat of it is in //3</p>
  
  <pre><code>//1
  - (NSData *)scaledImageDataWithSmallerDimension:(CGFloat)length compressionQuality:(float)quality
  {
      CGSize newSize = [self scaledSizeWithSmallerDimension:length];
      return [self imageDataResizedToFitSize:newSize compressionQuality:quality];
  }
  
  //2
  - (NSData *)imageDataResizedToFitSize:(CGSize)size compressionQuality:(float)quality
  {
      return [self thumbnailDataForAsset:self.asset maxPixelSize:MAX(size.height, size.width) compressionQuality:quality];
  }
  
  //3
  - (NSData *)thumbnailDataForAsset:(ALAsset *)asset
                       maxPixelSize:(NSUInteger)size
                 compressionQuality:(float)quality {
    @autoreleasepool {
      NSParameterAssert(asset != nil);
      NSParameterAssert(size &gt; 0);
  
      ALAssetRepresentation *rep = [asset defaultRepresentation];
  
      CGDataProviderDirectCallbacks callbacks = {
        .version = 0,
        .getBytePointer = NULL,
        .releaseBytePointer = NULL,
        .getBytesAtPosition = getAssetBytesCallback,
        .releaseInfo = releaseAssetCallback,
      };
  
      CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep),
                                                              [rep size],
                                                              &amp;callbacks);
      CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
  
      NSDictionary *imageOptions =
      @{
        (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
        (NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithUnsignedInteger:size],
        (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
        };
      CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source,
                                                                0,
                                                                (__bridge CFDictionaryRef) imageOptions);
      CFRelease(source);
      CFRelease(provider);
  
      if (!imageRef) {
        return nil;
      }
  
  
      NSMutableData *outputData = [[NSMutableData alloc] init];
      CGImageDestinationRef destRef = CGImageDestinationCreateWithData((__bridge CFMutableDataRef) outputData,
                                                                       kUTTypeJPEG,
                                                                       1,
                                                                       NULL);
      NSDictionary *imageAddOptions =
      @{
        (NSString *)kCGImageDestinationLossyCompressionQuality : [NSNumber numberWithFloat:quality],
        };
      CGImageDestinationAddImage(destRef,
                                 imageRef,
                                 (__bridge CFDictionaryRef) imageAddOptions);
      CGImageDestinationFinalize(destRef);
      CFRelease(imageRef);
      CFRelease(destRef);
  
      return outputData;
    }
  }
  
  // Helper methods for thumbnailDataForAsset:maxPixelSize:
  static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) {
      ALAssetRepresentation *rep = (__bridge id)info;
  
      NSError *error = nil;
      size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:count error:&amp;error];
  
      if (countRead == 0 &amp;&amp; error) {
          // We have no way of passing this info back to the caller, so we log it, at least.
          NSLog(@"thumbnailForAsset:maxPixelSize: got an error reading an asset: %@", error);
      }
  
      return countRead;
  }
  
  static void releaseAssetCallback(void *info) {
      // The info here is an ALAssetRepresentation which we CFRetain in thumbnailDataForAsset:maxPixelSize:.
      // This release balances that retain.
      CFRelease(info);
  }
  </code></pre>
  
  <p>Note that I started using these after I experienced a similar memory leak using UIImageJPEGRepresentation() on a method that turned the CGImageRef into a UIImageinstead.</p>
  
  <p>I'm pretty sure that there's a memory leak because the app eventually terminates due to a memory error.  When I run instruments on it, there are tons of living malloc 464/592/398 KB allocations, which instruments gives this stack for:</p>
  
  <pre><code>   0 libsystem_malloc.dylib malloc_zone_realloc
     1 libsystem_malloc.dylib realloc
     2 Foundation _NSMutableDataGrowBytes
     3 Foundation -[NSConcreteMutableData appendBytes:length:]
     4 ImageIO CGImageWriteSessionPutBytes
     5 ImageIO emptyOutputBuffer
     6 ImageIO encode_mcu_huff
     7 ImageIO compress_data
     8 ImageIO process_data_simple_main
     9 ImageIO _cg_jpeg_write_scanlines
    10 ImageIO writeOne
    11 ImageIO _CGImagePluginWriteJPEG
    12 ImageIO CGImageDestinationFinalize
    13 MyApp -[MyPhoto thumbnailDataForAsset:maxPixelSize:compressionQuality:] 
    14 MyApp -[MyPhoto imageDataResizedToFitSize:compressionQuality:] 
    15 MyApp -[MyPhoto scaledImageDataWithSmallerDimension:compressionQuality:] 
  </code></pre>
  
  <p>Is this a bug in CGImageDestinationFinalize or am I responsible for freeing something I'm not?</p>
  
  <p>Thanks in advance.</p>