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释放它不起作用.首先,没有"自动释放"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将其释放,这将传递地释放保留采取的块封闭self和image.
}
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阻止块关闭保留asset并image允许您自己明确地保留/释放它们.这意味着您创建的所有内容都将被明确处理.(rep并且image可能是保留/自动释放,但是你的游泳池应该照顾那些.)我相信这是最明确的你可以做到这一点,因为它asset被传递给你,因此你无法控制它的存在时间,它最终是存储在CGImageRef中的"所有者"(就此范围而言)iref.
希望能澄清一点事情.