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

时间:2015-01-24 21:56:16

标签: ios objective-c memory-leaks

我是乐器的新手,但我之前成功找到了泄漏。这一次,不是这样 - 每次调用此代码时都会有34MB的泄漏! 我已经尝试发布下面的所有相关代码,同时剪掉DDLogging等内容......

首先,仪器屏幕显示问题。请注意,我尝试模拟内存警告并等待一段时间,没有任何变化 - 这个内存被永久吃掉。

34mb memleak instruments screencap

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();
}

从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;
    }
}

...我记得要结束我的图片上下文并将其包装在autoreleasepool中......我错过了什么?

2 个答案:

答案 0 :(得分:1)

想出来。从上面的代码中,它是一个二级thead(实际上是一个NSOperation),它调用了PhotoManager:saveImage,它就是这样做的:

while ( true ) {
    // do work
    [PhotoManager singleton] saveImage:xyz];

    [NSThread sleepForTimeInterval:0.5];
}

问题在于虽然辅助线程的main()正在使用@autoreleasepool,但是该池永远没有时间流失,因为线程总是处于休眠或工作状态。

解决方案是使用这样的另一个@autorelease池,在其外面进行睡眠:

while ( true ) {
    @autoreleasepool {
        // do work
        [PhotoManager singleton] saveImage:xyz];
    }
    [NSThread sleepForTimeInterval:0.5];
}

答案 1 :(得分:0)

请改用CGImageRef。像这样:

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);