ImageWithCGImage: GCD memory issue

When I execute the following only on the main thread, iref immediately gets auto-implemented:

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

But when I execute imageWithCGImage: with a GCD in the background thread, iref will not be released instantly, as in the first example. only in 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]; }); } }); } 

How can I immediately make a CGImageRef object?

Previous research:

+1
source share
2 answers

First, there is no concept of a “self-implemented” CF object. You may find yourself in a situation where such a thing exists when working with free bridge classes, but, as you can see, there are CFRetain and CFRelease , but not CFAutorelease . Therefore, I think you are misinterpreting the ownership of iref . Let me track ownership of all of this code:

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

asset is passed to this method. It is assumed that its conservation rate is not less than 1.

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

Block closure is held on asset .

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

This returns to you, by naming convention, an object that you do not have. It can be auto-implemented, it can be singleton / global, etc. But you do not own it and should not own it, keeping it.

  CGImageRef iref = [rep fullScreenImage]; 

Since there is no concept of an “auto-implemented” CF object, we assume that rep returns you an internal pointer to the CGImageRef owned by rep . You also do not own it and should not keep it. You have no control when he leaves. It is reasonable to assume that it will live as long as rep and the reasonable guess is that rep will live as long as asset , so you probably assume that iref will live at least as long as asset .

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

If a UIImage needs a CGImageRef to stick to, it must save or make a copy to make sure it remains alive. (Probably the last one.) UIImage itself is auto-implemented by naming convention.

  dispatch_async(dispatch_get_main_queue(), ^(void) { 

This closure of the inner block will be held on image (and self ). The block will be copied by libdispatch, which extends the life of those that are stored until the block is executed and released.

  [self.imageView setImage:image]; 

In case it is necessary, the image should take (or copy) the image to image in order to perform its work.

  }); 

The indoor unit is running. At some point in the future, libdispatch will release it, which will lead to a transitive release of the save made by blocking the block on self and image .

  } 

Your auto pool appears here. Everything that was implicitly saved / auto-implemented should be released now.

  }); 

An external block is running. At some point in the future, libdispatch will release it, which will transitively release the save removed by closing the block to asset .

 } 

Ultimately, this method cannot control the lifetime of CGImageRef in iref , since it never owns it. This implies that the CGImageRef is in transit owned by the asset , so it will live at least as long as the asset . Since asset saved through use in an external block (i.e., It is saved when the external block is closed), and since libdispatch does not make promises about when the block lock will end, you can not guarantee that iref will leave before libdispatch approaches to him.

If you want to go to manual save / free and be as explicit as possible, you can 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 prevents saving asset and image block locks, allowing you to explicitly save / release them yourself. This will mean that everything you create will be clearly located. ( rep and image supposedly saved / auto-implemented, but your pool should take care of this.) I think this is the most obvious thing you can say about it, given that the asset is being transferred to you, and therefore you do not control how long it lives , and ultimately it is the "owner" (as far as the scope is concerned) of CGImageRef, stored in iref .

Hope this clarifies things a bit.

+9
source

You must save CGImageRef
..

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

The rest is just a matter of the launch cycle, in one without a GCD the image is released istanneously, and in the other it is controlled by the GCD, but in any case it is a mistake, someone should take responsibility for iref.

+2
source

Source: https://habr.com/ru/post/1208149/


All Articles