当我仅在主线程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
对象?
之前的研究:
CGImageRef
个对象,并且在它应该被释放后仍然存在大约一分钟。CGImageRef
手动释放CGImageRelease
对象,我会在图片尝试自动释放后一分钟后收到BAD_EXEC异常。iref
保留CGImageRetain
,然后使用CGImageRelease
发布它不起作用。答案 0 :(得分:9)
首先,没有“自动释放”CF对象的概念。在处理免费桥接类时,您可以进入存在此类事物的情况,但正如您所看到的,有CFRetain
和CFRelease
但没有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将发布它,它将传递释放self
和image
上的块闭包所保留的保留。
}
您的自动发布池会弹出此处。任何隐含保留/自动释放的东西都应该立即发布。
});
外部块已完成执行。在将来的某个时候,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
});
}
});
}
可以阻止块关闭保留__block
和asset
,允许您自己明确保留/释放它们。这意味着您创建的所有内容都将被明确处理。 (image
和rep
可能会被保留/自动释放,但是您的游泳池应该照顾那些。)我相信这是最明确的,因为image
已通过在你身上,因此你无法控制它的存在时间,它最终是存储在asset
中的CGImageRef的“所有者”(就这个范围而言)。
希望能澄清一点。
答案 1 :(得分:2)
你应该保留CGImageRef
..
CGImageRef iref = CGImageRetain([rep fullScreenImage]);
//..before [self.imageView..
CGImageRelease(iref)
剩下的只是一个运行循环的问题,在一个没有GCD的情况下,图像被释放,另一个是由GCD管理,但是无论如何,有人必须拥有iref的所有权。