imageWithCGImage:GCD内存问题

当我只在主线程执行以下操作时, iref立即获得autoreleased:

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

先前研究:

  • 当我用它logging时,泄漏仪器不显示任何泄漏。
  • 分配工具显示一个CGImageRef对象被分配,并且在应该被释放之后仍然活着一分钟左右。
  • 如果我尝试使用CGImageRelease手动释放CGImageRef对象, CGImageRelease在图像尝试获取自动释放后,我会在一分钟后得到一个BAD_EXECexception。
  • 使用CGImageRetain保留iref ,然后使用CGImageRelease释放它不起作用。
  • 类似的问题在stackoverflow没有帮助: 图像加载与gcd , 在创build图像收到内存警告, 从alaseet结果获取fullscreenimage时内存泄漏 ,

首先,没有一个“自动释放”CF对象的概念。 在处理免费的桥接类时,可能会遇到这种情况,但您可以看到,有一个CFRetain和一个CFRelease但没有CFAutorelease 。 所以我认为你是在误解iref的所有权。 让我们来追踪这个代码的所有权:

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

asset被传递到这个方法。 其保留数至less为1。

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

封锁收回asset

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

这将通过命名约定返回您不拥有的对象。 它可能是autoreleased,它可能是一个单身/全球等,但你不拥有它,不应该通过保留它拥有它。

  CGImageRef iref = [rep fullScreenImage]; 

由于没有“autoreleased”CF对象的概念,我们将假定rep向您返回一个指向由rep拥有的CGImageRef的内部指针。 你也不拥有这个,不应该保留它。 相关的,你不能控制什么时候会消失。 一个合理的猜测是,它会活到只要rep和一个合理的猜测是rep将生活只要asset所以你可能应该假设iref将至less与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]; 

如果需要,图像视图将对图像进行保留(或复制),以便完成其工作。

  }); 

内部块完成执行。 在将来的某个时候,libdispatch将会释放它,这会通过selfimage传递释放被闭包占用的保留。

  } 

你的autorelease池在这里popup。 任何隐式保留/自动释放的东西现在应该被释放。

  }); 

外部块完成执行。 在将来的某个时候,libdispatch会释放它,这将会过度释放由asset上的块closures所带来的保留。

 } 

最终,这个方法不能控制iref中的CGImageRef的生命周期,因为它永远不拥有它的所有权。 这里的含义是,CGImageRef是由asset过渡性拥有的,所以它至less会和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 }); } }); } 

__block可防止块closures保留assetimage使您可以明确地保留/释放它们。 这将意味着你所创造的所有东西,都将被明确处置。 ( repimage大概保留/自动释放,但你的池应该照顾这些)。我相信这是最明确的,你可以这样做,因为asset被传递给你,因此你不能控制它的存在时间很长,并且它最终是存储在iref中的CGImageRef的“所有者”(就这个范围而言)。

希望澄清一些事情。

你应该保留CGImageRef
..

 CGImageRef iref = CGImageRetain([rep fullScreenImage]); //..before [self.imageView.. CGImageRelease(iref) 

剩下的只是一个运行循环的问题,在没有GCD的情况下图像会被释放,另外一个是由GCDpipe理的,但是无论如何都是错误的,有人必须拥有iref的所有权。