imageWithCGImage:GCD内存问题 [英] imageWithCGImage: GCD memory issue
问题描述
当我仅在主线程上执行以下操作时,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
对象立即释放?
先前的研究:
- 我用泄漏仪器记录时没有泄漏.
- 分配工具显示
CGImageRef
对象已分配,并且在应释放后仍处于活动状态约一分钟. - 如果尝试使用
CGImageRelease
手动释放CGImageRef
对象,则在尝试自动释放图像的一分钟后,我收到了BAD_EXEC异常. - 使用
CGImageRetain
保留iref
,然后使用CGImageRelease
释放它不起作用. - 关于stackoverflow的类似问题没有帮助:使用gcd加载图像,在创建映像时收到内存警告,从alaseet结果获取全屏图像时内存泄漏,
- 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 usingCGImageRelease
I get a BAD_EXEC exception after a minute when the image tries to get autoreleased. - retaining the
iref
usingCGImageRetain
and then usingCGImageRelease
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对象的概念.在处理免费的桥接类时,您可能会遇到这样的情况,但是如您所见,这里有CFRetain
和CFRelease
但没有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会释放它,这将以传递方式释放self
和image
上的块闭包所占用的保留.
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
防止块闭包保留asset
和image
,从而允许您自己明确地保留/释放它们.这意味着在您创建的所有事物中,所有事物都将被明确处理. (rep
和image
可能会保留/自动释放,但是您的存储池应该负责这些操作.)我相信,鉴于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屋!