imageWithCGImage:GCD内存问题 [英] imageWithCGImage: GCD memory issue

查看:289
本文介绍了imageWithCGImage:GCD内存问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When I perform the following only on the main thread iref gets autoreleased immediately:

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

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

But when I perform imageWithCGImage: with GCD on a background thread iref does not get released instantly like in the first example. only after about a minute:

-(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对象立即释放?

先前的研究:

  • The Leak instrument doesnt show any leaks when I record with it.
  • The Allocations instruments shows that a CGImageRef object was allocated and is still living for about a minute after it should have been released.
  • If I try to manually release the CGImageRef object using CGImageRelease I get a BAD_EXEC exception after a minute when the image tries to get autoreleased.
  • retaining the iref using CGImageRetain and then using CGImageRelease to release it doesnt work.
  • similar questions on stackoverflow that didn't help: image loading with gcd, received memory warning in create image, memory leak when get fullscreenimage from alaseet result,

推荐答案

首先,没有自动释放" CF对象的概念.在处理免费的桥接类时,您可能会遇到这样的情况,但是如您所见,这里有CFRetainCFRelease但没有CFAutorelease.因此,我认为您误解了iref的所有权.让我们跟踪这段代码的所有权:

First off, there's not a notion of an "autoreleased" CF object. You can get into situations where such a thing exists when dealing with toll-free bridged classes, but as you can see, there's a CFRetain and a CFRelease but no CFAutorelease. So I think you're misconstruing the ownership of iref. Let's trace ownership throughout this code:

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

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

asset is passed into this method. Its retain count is presumed to be at least 1.

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

块关闭保留在asset上.

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

通过命名约定,这将返回您不拥有的对象.它可能是自动发布的,可能是单例/全局的,等等.但是您不拥有它,也不应该通过保留它来拥有它的所有权.

This returns you, by naming convention, an object that you don't own. It might be autoreleased, it might be a singleton/global, etc. But you don't own it and shouldn't take ownership of it by retaining it.

            CGImageRef iref = [rep fullScreenImage];

由于没有自动释放" CF对象的概念,因此我们假定rep正在向您返回指向rep所拥有的CGImageRef的内部指针.您也不拥有它,也不应该保留它.相关地,您无法控制何时消失.一个合理的猜测是它的寿命将长至rep,一个合理的猜测是rep的寿命将长至asset,因此您可能应该假定iref的寿命至少与.

Since there's not a notion of an "autoreleased" CF object, we'll presume that rep is returning you an inner pointer to a CGImageRef owned by rep. You also don't own this, and shouldn't retain it. Relatedly you don't control when it will go away. A reasonable guess would be that it will live as long as rep and a reasonable guess is that rep will live as long as asset so you should probably assume that iref will live at least as long as asset.

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

如果UIImage需要CGImageRef保留下来,它将保留或进行复制以确保其有效. (可能是后者.)UIImage本身是通过命名约定自动发布的.

If the UIImage needs the CGImageRef to stick around, it will take a retain or make a copy to ensure that it stays alive. (Probably the latter.) The UIImage itself is autoreleased, by naming convention.

            dispatch_async(dispatch_get_main_queue(), ^(void) {

此内部块关闭将保留在image(和self)上.将通过libdispatch复制该块,以延长保留的寿命,直到执行该块并将其自身释放为止.

This inner block closure is going to take a retain on image (and self). The block will be copied by libdispatch extending the life of those retains until the block is executed and itself released.

                [self.imageView setImage:image];

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

The image view is going to take a retain (or copy) on image if it needs to, in order to do its job.

            });

内部块执行完毕.将来的某个时候,libdispatch会释放它,这将以传递方式释放selfimage上的块闭包所占用的保留.

The inner block is done executing. At some point in the future, libdispatch will release it, which will transitively release the retains taken by the block closure on self and image.

        }

您的自动释放池将在此处弹出.任何隐式保留/自动释放的内容都应立即释放.

Your autorelease pool pops here. Anything that was implicitly retain/autoreleased should be released now.

    });

外部块执行完毕.将来的某个时候,libdispatch将释放它,这将可传递地释放asset上的块关闭所占用的保留.

The outer block is done executing. At some point in the future, libdispatch will release it, which will transitively release the retain taken by the block closure on asset.

}

最终,此方法无法控制iref中CGImageRef的生存期,因为它从来没有所有权.这里的含义是CGImageRef由asset可传递地拥有,因此它的生存期至少与asset一样长.由于asset是通过在外部块中使用而保留的(即,由外部块的闭合保留),并且由于libdispatch对完成的块何时被释放没有任何保证,因此您实际上不能保证iref将被释放libdispatch早日解决它.

Ultimately, this method cannot control the lifetime of the CGImageRef in iref because it never has ownership of it. The implication here is that the CGImageRef is transitively owned by asset, so it will live at least as long as asset. Since asset is retained by virtue of being used in the outer block (i.e retained by the outer block's closure) and since libdispatch makes no promises about when finished blocks will be released, you effectively can't guarantee that iref will go away any sooner than libdispatch gets around to it.

如果您想进行手动保留/发布并尽可能地明确,则可以执行以下操作:

If you wanted to go to manual retain/release, and be as explicit about it as possible, you could do this:

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

__block防止块闭包保留assetimage,从而允许您自己明确地保留/释放它们.这意味着在您创建的所有事物中,所有事物都将被明确处理. (repimage可能会保留/自动释放,但是您的存储池应该负责这些操作.)我相信,鉴于asset被传递给您,因此这可能是最明确的了,因此您无需控制它的生存时间,它最终是iref中存储的CGImageRef的所有者"(就此范围而言).

__block prevents the block closures from retaining asset and image allowing you to explicitly retain/release them yourself. This will mean that of all the things you create, all will be explicitly disposed. (rep and image are presumably retain/autoreleased, but your pool should take care of those.) I believe this is the most explicit you can be about this, given that asset is passed in to you, and therefore you don't control how long it lives, and it is ultimately the "owner" (as far as this scope is concerned) of the CGImageRef stored in iref.

希望可以澄清一些事情.

Hope that clarifies things a bit.

这篇关于imageWithCGImage:GCD内存问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆