我正在构建一个应用程序,将许多图像的已调整大小的版本上传到服务器,我似乎有一个内存泄漏,我无法追踪。这是我创建多部分请求的方法(使用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转换为UIImageinstead的方法进行了类似的内存泄漏后,我开始使用这些内容。
我非常确定存在内存泄漏,因为应用程序最终因内存错误而终止。当我运行仪器时,有大量的生活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中的错误还是我负责释放我不喜欢的东西?
提前致谢。
答案 0 :(得分:0)
为什么在框架已经提供的时候需要使用如此复杂的东西。 ImageIO框架具有一些功能,可以让您从源图像创建缩略图。
CGImageSourceRef src = CGImageSourceCreateWithURL(url);
NSDictionary *options = (CFDictionaryRef)[NSDictionary
dictionaryWithObject:[NSNumber numberWithInt:1024
forKey:(id)kCGImageSourceThumbnailMaxPixelSize];
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src,0,options);
UIImage *image = [UIImage imageWithCGImage:thumbnail];
CGImageRelease(thumbnail);
CGImageSourceRelease(src);
答案 1 :(得分:0)
我仔细查看了你的代码,内存管理看起来很合理。你有@autoreleasepool块中的密钥位,所以应该清理所有自动释放的对象。您对CF对象的处理看起来也是正确的。 (不可否认,内存错误很难发现......)
如果在使用系统方法UIImageJPEGRepresentation时遇到类似的泄漏,它会开始闻起来越来越像系统错误。您是否针对不同的OS版本(在设备上)进行了测试。这可能是这个泄漏对iOS 7来说是新的,并且不会出现在旧的iOS版本中。如果是这样,重要的是让Apple快速意识到这个错误,以便他们实际修复它,而不是在iOS 8发布之前忽略它。
你有没有在设备和SIM卡上测试它?我在过去不止一次花了很多时间在sim上运行我的代码时试图追踪明显的内存泄漏,只是得出结论,这个bug实际上是在系统库的模拟器实现中,而且完全相同在设备上运行时代码没有泄漏。
我建议向Apple的bug报告系统提交2个错误:UIImageJPEGRepresentation
方法的泄漏,以及CGImageSourceCreateThumbnailAtIndex
方法的泄漏。在这两种情况下提供最少的测试应用程序,或Apple的精彩(阅读无知)错误筛选器将立即回复给您。
一旦前线漏洞检查员用尽了愚蠢的理由将其反馈给您,他们应该将其提交给真正的工程师,他们可以确定它是否真的是一个框架错误。 (你能告诉我对Apple的第一级漏洞筛选器的看法不高吗?)