imageWithCGImage:GCD内存问题

Tom*_*ven 1 memory core-graphics objective-c grand-central-dispatch ios

当我只在主线程上执行以下操作时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];
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我执行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];
            });
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

如何CGImageRef立即释放对象?

之前的研究:

  • 当我用它记录时,泄漏仪器不显示任何泄漏.
  • 分配工具表明,一个CGImageRef物体已被分配,并且在它应该被释放后仍然存活了大约一分钟.
  • 如果我尝试手动释放CGImageRef对象,CGImageRelease我会在图像尝试自动释放一分钟后获得BAD_EXEC异常.
  • 保留iref使用CGImageRetain然后使用CGImageRelease释放它不起作用.
  • 计算器上没有帮助类似的问题:图像加载与GCD,收到内存警告在创建图像,内存泄漏时从alaseet结果GET fullscreenimage,

ipm*_*mcc 9

首先,没有"自动释放"CF对象的概念.在处理免费桥接类时,您可以进入存在这种情况的情况,但正如您所看到的,有CFRetain一个CFRelease但是没有CFAutorelease.所以我认为你误解了所有权iref.让我们在整个代码中跟踪所有权:

-(void)loadImage:(ALAsset*)asset{
Run Code Online (Sandbox Code Playgroud)

asset传递给这个方法.其保留计数假定至少为1.

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
Run Code Online (Sandbox Code Playgroud)

块闭合需要保留asset.

        @autoreleasepool {
            ALAssetRepresentation* rep = [asset defaultRepresentation];
Run Code Online (Sandbox Code Playgroud)

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

            CGImageRef iref = [rep fullScreenImage];
Run Code Online (Sandbox Code Playgroud)

由于没有"自动释放"CF对象的概念,我们假设它rep会返回一个指向CGImageRef拥有者的内部指针rep.你也不拥有它,也不应该保留它.相关地,你无法控制什么时候会消失.一个合理的猜测是,它会活着,rep并且一个合理的猜测rep将会存活,只要asset这样你应该假设iref它将至少活得有效asset.

            UIImage* image = [UIImage imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];
Run Code Online (Sandbox Code Playgroud)

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

            dispatch_async(dispatch_get_main_queue(), ^(void) {
Run Code Online (Sandbox Code Playgroud)

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

                [self.imageView setImage:image];
Run Code Online (Sandbox Code Playgroud)

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

            });
Run Code Online (Sandbox Code Playgroud)

内部块完成执行.在未来的某个时刻,libdispatch将其释放,这将传递地释放保留采取的块封闭selfimage.

        }
Run Code Online (Sandbox Code Playgroud)

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

    });
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

最终,此方法无法控制CGImageRef的生命周期,iref因为它永远不会拥有它.这里的含义是CGImageRef是由传递所拥有的asset,所以它至少会存在asset.因为asset在外部块中使用(即由外部块的闭包保留)而保留,并且由于libdispatch没有做出关于何时释放完成块的承诺,所以实际上不能保证iref会比libdispatch更快地消失到处都是.

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

-(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
            });
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

__block阻止块关闭保留assetimage允许您自己明确地保留/释放它们.这意味着您创建的所有内容都将被明确处理.(rep并且image可能是保留/自动释放,但是你的游泳池应该照顾那些.)我相信这是最明确的你可以做到这一点,因为它asset被传递给你,因此你无法控制它的存在时间,它最终是存储在CGImageRef中的"所有者"(就此范围而言)iref.

希望能澄清一点事情.