【发布时间】:2014-06-23 14:01:04
【问题描述】:
我正在构建一个应用程序,该应用程序将许多图像的调整大小版本上传到服务器,但我似乎有无法追踪的内存泄漏。这是我创建多部分请求的方法(使用 Restkit 0.2):
[self.objectManager
multipartFormRequestWithObject:nil
method:RKRequestMethodPUT
path:pathString
parameters:@{@"photo": photoParamater}
constructingBodyWithBlock:^(id<AFMultipartFormData> 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"];
}
}
}];
这是从后台队列上的 NSOperation 的 main() 函数调用的方法的一部分。
以下是获取实际照片数据的照片方法。它的肉在 //3
//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 > 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],
&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:&error];
if (countRead == 0 && 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);
}
请注意,在我使用 UIImageJPEGRepresentation() 在将 CGImageRef 转换为 UIImage 的方法上遇到类似的内存泄漏后,我开始使用这些。
我很确定存在内存泄漏,因为应用程序最终会因内存错误而终止。当我在其上运行仪器时,有大量的活 malloc 464/592/398 KB 分配,仪器为此堆栈提供了:
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:]
这是 CGImageDestinationFinalize 中的一个错误,还是我负责释放我不是的东西?
提前致谢。
【问题讨论】:
-
我最初的反应是,您正在创建大量图像并将它们保存在内存中以供传输(
appendPartData部分)您是否可能只是内存不足而没有实际泄漏?您可以将单个图像写入临时文件,然后使用 RestKit 上传这些图像吗? -
感谢大卫,但多部分表单请求一次只能发布一张图片。有 2 个线程正在上传,因此在任何给定时间内存中应该只有 2 个图像。在我的 5S 上,它在内存不足之前成功地通过了数百张图像。我还有第二个函数,它做几乎完全相同的事情,但同时发布了一批 100 个缩略图,并且 not 似乎触发了这个泄漏,这让我更加困惑。
-
我正在尝试在 OSX 上做一些几乎相同的事情并观察到同样的问题。您是否找到解决方法或以其他方式解决此问题?
-
抱歉,我不记得了,我很快就停止了上传所有缩略图的应用程序。
标签: ios objective-c memory memory-leaks core-graphics