【问题标题】:UIGraphicsGetImageFromCurrentImageContext() - Memory LeakUIGraphicsGetImageFromCurrentImageContext() - 内存泄漏
【发布时间】:2013-11-26 20:40:54
【问题描述】:

我使用UIImagePickerControllerSourceTypeCamera 和自定义cameraOverlayView 打开相机,这样我就可以在没有“使用照片”步骤的情况下拍摄多张照片。

这很好用,但保存照片功能存在内存泄漏。通过大量调试和研究,我将其范围缩小到 UIGraphicsGetImageFromCurrentImageContext 函数。

这是发生的代码的 sn-p:

UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]);
[[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

我搜索了互联网,似乎UIGraphicsGetImageFromCurrentImageContext() 函数(引用自this SO question“返回一个新的自动发布的UIImage 并将finalTimestampImage ivar 指向它。之前分配的@987654329 @ 从未真正释放,它的变量只是重新指向其他地方。”

我尝试了很多显然对其他人有效的解决方案:

  • UIGraphicsEndImageContext之后添加timestampedImage.layer.contents = nil;

  • UIGraphicsEndImageContext之后添加CGContextRef context = UIGraphicsGetCurrentContext();CGContextRelease(context);

  • 将上述 sn-p 包装在 NSAutoreleasePool

  • 将整个 saveThisPhoto 函数包装在 NSAutoreleasePool

  • 相机弹出时创建NSAutoreleasePool,调用didReceiveMemoryWarning时调用[pool release]

  • 在调用didReceiveMemoryWarning 时关闭摄像头弹出窗口,希望它能清除池

  • 以上所有可能的组合

然而我尝试的一切,当我在设备上反复拍照时,我可以看到Memory Utilized上升而不是下降。

有谁知道如何释放UIGraphicsGetImageFromCurrentImageContext 创建的自动释放对象?

或者,是否有另一种方法可以从UIImageView 中创建UIImage

编辑:

这里是所要求的全部功能。那里添加了很多额外的版本,只是为了确保一切都被清理干净。我已经系统地测试了saveThisPhoto中每个代码块的内存泄漏,它只发生在UIGraphicsGetImageFromCurrentImageContext块(上面的sn-p)运行时。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    NSLog(@"SAVING PHOTO");

    [self saveThisPhoto:info];

    picker = nil;
    [picker release];

    info = nil;
    [info release];

}

- (void)saveThisPhoto:(NSDictionary *)info {

    // Get photo count for filename so we're not overriding photos

    int photoCount = 0;

    if ([[NSUserDefaults standardUserDefaults] objectForKey:@"photocount"]) {
        photoCount= [[[NSUserDefaults standardUserDefaults] objectForKey:@"photocount"] intValue];
        photoCount++;
    }

    [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%d", photoCount] forKey:@"photocount"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    // Obtaining saving path

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *fileName = [NSString stringWithFormat:@"ri_%d.jpg", photoCount];
    NSString *fileNameThumb = [NSString stringWithFormat:@"ri_%d_thumb.jpg", photoCount];
    NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:fileName];
    NSString *imagePathThumb = [documentsDirectory stringByAppendingPathComponent:fileNameThumb];

    // Extracting image from the picker and saving it

    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    // SAVE TO IPAD AND DB

    if ([mediaType isEqualToString:@"public.image"]){

        // Get Image

        UIImage *editedImage = [info objectForKey:UIImagePickerControllerOriginalImage];

        // Figure out image orientation

        CGSize resizedSize;
        CGSize thumbSize;

        if (editedImage.size.height > editedImage.size.width) {
            resizedSize = CGSizeMake(480, 640);
            thumbSize = CGSizeMake(150, 200);
        } else {
            resizedSize = CGSizeMake(640, 480);
            thumbSize = CGSizeMake(150, 113);
        }

        // MAKE NORMAL SIZE IMAGE

        UIImage *editedImageResized = [editedImage resizedImage:resizedSize interpolationQuality:0.8];

        // clean up the one we won't use any more

        editedImage = nil;
        [editedImage release];

        // ADD TIMESTAMP TO IMAGE

        // make the view

        UIImageView *timestampedImage = [[UIImageView alloc] initWithImage:editedImageResized];
        CGRect thisRect = CGRectMake(editedImageResized.size.width - 510, editedImageResized.size.height - 30, 500, 20);

        // clean up

        editedImageResized = nil;
        [editedImageResized release];

        // make the label

        UILabel *timeLabel = [[UILabel alloc] initWithFrame:thisRect];
        timeLabel.textAlignment =  UITextAlignmentRight;
        timeLabel.textColor = [UIColor yellowColor];
        timeLabel.backgroundColor = [UIColor clearColor];
        timeLabel.font = [UIFont fontWithName:@"Arial Rounded MT Bold" size:(25.0)];
        timeLabel.text = [self getTodaysDateDatabaseFormat];
        [timestampedImage addSubview:timeLabel];

        // clean up what we won't use any more

        timeLabel = nil;
        [timeLabel release];

        // make UIIMage out of the imageview -- MEMORY LEAK LOOKS LIKE IT IS IN THIS BLOCK

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]);
        [[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        timestampedImage.layer.contents = nil;
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextRelease(context);

        // clean up the one we won't use any more

        timestampedImage = nil;
        [timestampedImage release];

        // SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER

        NSData *webDataResized = UIImageJPEGRepresentation(finalTimestampImage, 1.0); // JPG

        [webDataResized writeToFile:imagePath atomically:YES];

        // clean up the one we won't use any more

        finalTimestampImage = nil;
        [finalTimestampImage release];

        [pool release]; // to get rid of the context image that is stored

        // SAVE TO DATABASE

        [sqlite executeNonQuery:@"INSERT INTO inspection_images (agentid,groupid,inspectionid,areaid,filename,filenamethumb,filepath,orderid,type) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?) ",
         [NSNumber numberWithInt:loggedIn],
         [NSNumber numberWithInt:loggedInGroup],
         myInspectionID,
         [[tableData objectAtIndex:alertDoMe] objectForKey:@"areaid"],
         fileName,
         fileNameThumb,
         documentsDirectory,
         [NSNumber numberWithInt:photoCount],
         [NSNumber numberWithInt:isPCR]
         ];

        // Clean up

        webDataResized = nil;
        [webDataResized release];

    } else {

        NSLog(@">>> IMAGE ***NOT*** SAVED");

    }

    NSLog(@"IMAGE SAVED - COMPLETE");

    info = nil;
    [info release];

}

【问题讨论】:

  • 您是否确认问题不在于使用finalTimestampImage 图像的进一步代码?也许某处有一个保留周期。
  • finalTimestampImage 用于得到一个UIImageJPEGRepresentation 保存到设备,那么它们都是released。我仍然相信这是另一个 SO 问题中提到的 UIGraphicsGetImageFromCurrentImageContext 函数创建的偷偷摸摸的 UIImage
  • 不,您误解了前面的 SO 问题。 “先前分配的 UIImage”是 _renderImage = [[UIImage alloc] init]; 中的 UIImage,它在 _renderImage = UIGraphicsGetImageFromCurrentImageContext(); 中泄漏。如果您像在 sn-p 中那样声明变量,它就不会泄漏。也许您可以发布更完整的代码示例。
  • 感谢您的浏览,如果您有时间可以浏览,我已经为上面的问题添加了完整的功能。所以你相信,因为我从像 UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext(); 这样的函数中得到了 UIImage(与另一个问题中的 alloc] init] 相反)它不应该泄漏?

标签: ios objective-c memory-leaks


【解决方案1】:

您在释放变量之前将它们设置为 nil,并且有些变量已经自动释放。

通常在使用 release 时,您应该将它们设置为 nil。

[var release]
var = nil;

但在其中一些你不应该调用释放。

以下是你的罪魁祸首。

    // clean up the one we won't use any more

    timestampedImage = nil;
    [timestampedImage release];

    // SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER

【讨论】:

  • 谢谢你,不知道我是怎么弄乱 nil/release 顺序的。我只发布timeLabeltimestampedImage,因为它们是我手动alloc 的。我现在可以重复拍摄照片而不会出现任何泄漏 - 只需同时拍摄 225 张照片,并且可以继续拍摄!再次感谢!
  • 在iOS8中,使用ARC如何处理“UIGraphicsGetImageFromCurrentImageContext()”。它总是返回“nil”。
猜你喜欢
  • 2011-07-04
  • 2011-11-03
  • 2011-11-18
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 2013-01-20
  • 2011-10-31
相关资源
最近更新 更多