imageWithCGImage:GCD内存问题

时间:2013-10-23 11:43:40

标签: ios objective-c memory core-graphics grand-central-dispatch

当我仅在主线程iref上执行以下操作时,会立即自动释放:

-(void)loadImage:(ALAsset*)asset{
    @autoreleasepool {
        ALAssetRepresentation* rep = [asset defaultRepresentation];
        CGImageRef iref = [rep fullScreenImage];
        UIImage* image = [UIImage imageWithCGImage:iref
                                             scale:[rep scale]
                                       orientation:UIImageOrientationUp];

        [self.imageView setImage:image];
    }
}

但是当我执行imageWithCGImage时:在后台线程上使用GCD iref不会像第一个例子那样立即释放。大约一分钟后:

-(void)loadImage:(ALAsset*)asset{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        @autoreleasepool {
            ALAssetRepresentation* rep = [asset defaultRepresentation];
            CGImageRef iref = [rep fullScreenImage];
            UIImage* image = [UIImage imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];

            dispatch_async(dispatch_get_main_queue(), ^(void) {
                [self.imageView setImage:image];
            });
        }
    });
}

如何立即释放CGImageRef对象?

之前的研究:

2 个答案:

答案 0 :(得分:9)

首先,没有“自动释放”CF对象的概念。在处理免费桥接类时,您可以进入存在此类事物的情况,但正如您所看到的,有CFRetainCFRelease但没有CFAutorelease。所以我认为你误解了iref的所有权。让我们在整个代码中追踪所有权:

-(void)loadImage:(ALAsset*)asset{

asset传递给此方法。其保留计数假定至少为1。

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {

块闭包在asset上保留。

        @autoreleasepool {
            ALAssetRepresentation* rep = [asset defaultRepresentation];

这将通过命名约定返回您不拥有的对象。它可能是自动释放的,可能是单身/全局等等。但是你不拥有它,不应该通过保留它来获得它的所有权。

            CGImageRef iref = [rep fullScreenImage];

由于没有“自动释放”CF对象的概念,我们假设rep正在返回一个指向CGImageRef拥有的rep的内部指针。你也不拥有它,也不应该保留它。相关地,你无法控制什么时候会消失。一个合理的猜测是,只要rep它就会生存,并且合理的猜测是rep只会asset生存,所以您应该假设iref将会至少与asset一样长。

            UIImage* image = [UIImage imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];

如果UIImage需要CGImageRef,那么它将需要保留或复制以确保它保持活着状态。 (可能是后者。)UIImage本身是通过命名约定自动释放的。

            dispatch_async(dispatch_get_main_queue(), ^(void) {

这个内部块闭包将在image(和self)上保留。该块将被libdispatch复制,延长这些保留的生命周期,直到块被执行并自行释放。

                [self.imageView setImage:image];

如果需要,图像视图将在image上保留(或复制),以便完成其工作。

            });

内部块已完成执行。在将来的某个时候,libdispatch将发布它,它将传递释放selfimage上的块闭包所保留的保留。

        }

您的自动发布池会弹出此处。任何隐含保留/自动释放的东西都应该立即发布。

    });

外部块已完成执行。在将来的某个时候,libdispatch将释放它,它将传递释放asset上的块闭包所保留的保留。

}

最终,此方法无法控制iref中CGImageRef的生命周期,因为它永远不会拥有它。这里的含义是CGImageRef由asset传递,因此它至少与asset一样长。因为asset由于在外部块中使用而保留(即由外部块的闭包保留),并且因为libdispatch没有做出关于何时释放完成块的承诺,所以实际上不能保证{{1}除了libdispatch之外,它将会消失。

如果您想要手动保留/发布,并尽可能明确地说明,可以这样做:

iref

-(void)loadImage:(ALAsset*)asset{ __block ALAsset* weakAsset = [asset retain]; // asset +1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { @autoreleasepool { ALAssetRepresentation* rep = [weakAsset defaultRepresentation]; CGImageRef iref = [rep fullScreenImage]; UIImage* image = [[UIImage alloc] imageWithCGImage:iref scale:[rep scale] orientation:UIImageOrientationUp]; __block UIImage* weakImage = [image retain]; // image +1 [weakAsset release]; // asset -1 dispatch_async(dispatch_get_main_queue(), ^(void) { [self.imageView setImage: weakImage]; [weakImage release]; // image -1 }); } }); } 可以阻止块关闭保留__blockasset,允许您自己明确保留/释放它们。这意味着您创建的所有内容都将被明确处理。 (imagerep可能会被保留/自动释放,但是您的游泳池应该照顾那些。)我相信这是最明确的,因为image已通过在你身上,因此你无法控制它的存在时间,它最终是存储在asset中的CGImageRef的“所有者”(就这个范围而言)。

希望能澄清一点。

答案 1 :(得分:2)

你应该保留CGImageRef
..

CGImageRef iref = CGImageRetain([rep fullScreenImage]);
//..before [self.imageView..
CGImageRelease(iref)

剩下的只是一个运行循环的问题,在一个没有GCD的情况下,图像被释放,另一个是由GCD管理,但是无论如何,有人必须拥有iref的所有权。