Ios ARC:通过块调用drawInRect查找内存泄漏

Ios ARC:通过块调用drawInRect查找内存泄漏,ios,objective-c,memory-leaks,Ios,Objective C,Memory Leaks,我对仪器不熟悉,但我以前成功地发现了漏洞。这次不是这样,每次我调用这个代码都会有34MB的泄漏! 我试着在剪掉诸如DDLogging之类的东西的同时发布下面所有的相关代码 AirprintCollagePrinter.m: - (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size { // do not upscale @autoreleasepool { if( image.siz

我对仪器不熟悉,但我以前成功地发现了漏洞。这次不是这样,每次我调用这个代码都会有34MB的泄漏! 我试着在剪掉诸如DDLogging之类的东西的同时发布下面所有的相关代码

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
首先,仪器屏幕显示问题。注意,我试着模拟一个内存警告,并且等待了一段时间,没有任何变化——这个内存被永久地消耗掉了

PhotoManager.m:
- (void)saveImage:(UIImage*)unimage completionBlock:(void(^)(BOOL success, NSError *error))completionBlock {

    __weak typeof(self) weakSelf = self;

    dispatch_block_t work = ^{
        __block UIImage* image = [[AirprintCollagePrinter singleton] fixRotation:unimage];

        // if trial mode, always downsize and watermark image
        if( [PurchaseHelper singleton].getPurchaseLevel == PurchaseLevelTrial ) {

            dispatch_sync(_photoManagerQueue, ^{
                if( image.size.height > 1800 || image.size.width > 1200 ) {
                    image = [[AirprintCollagePrinter singleton] fitImage:image scaledToFillSize:CGSizeMake(1200, 1800)];
                }

                image = [weakSelf imageWithWatermark:image withWatermark:_watermarkImage];
            });
        }

        <snip>
    };

    if( [[NSThread currentThread] isMainThread] )
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), work);
    else
        work();
}
AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
PhotoManager.m:
-(void)saveImage:(UIImage*)unimage completionBlock:(void(^)(BOOL success,NSError*error))completionBlock{
__弱类型(自我)弱自我=自我;
调度块工作=^{
__块UIImage*图像=[[AirprintCollagePrinter singleton]固定旋转:unimage];
//如果是试用模式,请始终缩小尺寸并对图像添加水印
if([PurchaseHelper singleton].getPurchaseLevel==PurchaseLevel试用版){
调度同步(_PhotoManager队列^{
如果(image.size.height>1800 | | image.size.width>1200){
image=[[AirprintCollagePrinter singleton]fitImage:image scaledToFillSize:CGSizeMake(12001800)];
}
image=[weakSelf imageWithWatermark:image withWatermark:_watermarkImage];
});
}
};
如果([[NSThread currentThread]isMainThread])
调度异步(调度获取全局队列(调度队列优先级默认为0),工作);
其他的
工作();
}
从Instruments screencap中,您可以看到AirprintCollagePrinter:fitImage是跟踪中我的代码的最后一部分,下面是:

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
AirprintCollagePrinter.m:
-(UIImage*)fitImage:(UIImage*)图像缩放的文件大小:(CGSize)大小{
//不要买高档货
@自动释放池{

如果(image.size.width从上面的代码中找到了它,那么调用PhotoManager:saveImage的是一个辅助thead(实际上是一个NSOperation),它是这样做的:

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
while ( true ) {
    // do work
    [PhotoManager singleton] saveImage:xyz];

    [NSThread sleepForTimeInterval:0.5];
}
CGImageRef imageRef = image.CGImage;
                CGColorSpaceRef colorspaceRef =CGColorSpaceCreateDeviceRGB();
                CFAutorelease(colorspaceRef);


                size_t width = itemSize.width;
                size_t height = itemSize.height;
                size_t bytesPerRow = kBytesPerPixel * width;

                CGContextRef context = CGBitmapContextCreate(NULL,
                                                             width,
                                                             height,
                                                             kBitsPerComponent,
                                                             bytesPerRow,
                                                             colorspaceRef,
                                                             kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);

                // Draw the image into the context and retrieve the new bitmap image without alpha
                CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
                CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
                image = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];

                CGContextRelease(context);
                CGImageRelease(imageRefWithoutAlpha);
问题是,尽管次线程的main()像预期的那样使用@autoreleasepool,但该池从来没有时间排空,因为该线程总是处于睡眠或工作状态

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
解决方案是使用另一个@autorelease池,如下所示,并在其外部进行睡眠:

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
while ( true ) {
    @autoreleasepool {
        // do work
        [PhotoManager singleton] saveImage:xyz];
    }
    [NSThread sleepForTimeInterval:0.5];
}

改用CGImageRef。类似如下:

AirprintCollagePrinter.m:
- (UIImage *)fitImage:(UIImage *)image scaledToFillSize:(CGSize)size {
    // do not upscale

    @autoreleasepool {
        if( image.size.width <= size.width && image.size.height <= size.height )
            return image;

        CGFloat scale = MIN(size.width/image.size.width, size.height/image.size.height);
        CGFloat width = image.size.width * scale;
        CGFloat height = image.size.height * scale;
        CGRect imageRect = CGRectMake(0,
                                      0,
                                      width,
                                      height);

        UIGraphicsBeginImageContextWithOptions(size, YES, 0);
        [image drawInRect:imageRect];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
}
while ( true ) {
    // do work
    [PhotoManager singleton] saveImage:xyz];

    [NSThread sleepForTimeInterval:0.5];
}
CGImageRef imageRef = image.CGImage;
                CGColorSpaceRef colorspaceRef =CGColorSpaceCreateDeviceRGB();
                CFAutorelease(colorspaceRef);


                size_t width = itemSize.width;
                size_t height = itemSize.height;
                size_t bytesPerRow = kBytesPerPixel * width;

                CGContextRef context = CGBitmapContextCreate(NULL,
                                                             width,
                                                             height,
                                                             kBitsPerComponent,
                                                             bytesPerRow,
                                                             colorspaceRef,
                                                             kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);

                // Draw the image into the context and retrieve the new bitmap image without alpha
                CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
                CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
                image = [UIImage imageWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];

                CGContextRelease(context);
                CGImageRelease(imageRefWithoutAlpha);

这个问题是在真实设备上还是在模拟器上?模拟器往往存在真实设备上不存在的泄漏。在真实设备上测试。它是在带有iOS 8.1.2No的ipad air上,这对释放内存没有任何影响。因为上面的代码在一个块中,[image drawInRect:imageRect]在块完成后自动释放,这需要时间。但是上面的代码将立即释放,并在一定时间内对内存消耗产生很大影响。但是正如您所说,最终,它们是相同的。