When to release the UIImage?
Asked Answered
R

4

5

I use the following code to draw a subimage

UIImage* subIm = getSubImage( large, rect );
[subIm drawInRect:self.bounds];

where getSubImage is defined as follows

    UIImage* getSubImage(UIImage* uim, CGRect rc){
      CGImageRef imref  = CGImageCreateWithImageInRect(uim.CGImage, rc); 
      UIImage*   sub = [UIImage imageWithCGImage:imref];
      CGImageRelease(imref);
        NSLog(@"subimage retainCount=%d", [sub  retainCount]); // is 1
      return sub;
   }//getSubImage

Is the code correct?

Is it safe to "CGImageRelease" imref?

Has sub "CGImageRetained" imref?

Should I release subIm (I get an error if I do)?

Is subIm contained in the autorelease-pool, and , if so, how do I know this?

In general, can one check if an object is contained in the autorelease pool (for debugging purpose)?

Ransome answered 28/12, 2010 at 13:21 Comment(1)
Do not call retainCount. The number returned is generally meaningless.Navarro
S
4

SubIm is enrolled in the AutoRelease Pool because this object is in fact created by the class method imageWithCGImage and that the rule is that instances created by class methods should always return autoreleased instances.

The code is correct though I don't understand why your are using the C syntax instead of the Obj-C syntax to define your function

Sermon answered 28/12, 2010 at 13:27 Comment(6)
Thank you for that rule, it will help! I still wonder if sub has "CGImageRetained" imref... The documentation only says that imref is not cached...Ransome
I thing it has retained it and your are supposed to release it. I have no idea why retainCount is still 1 after CGImageRelease and it might be that CGImageRelease add to release pool.. I don't know ! Check before and after the release maybe ?Sermon
Well, it is the sub-image's retainCount and it should be 1 if it is in the AutoRelease pool. I do not know how to check the retainCount of imref because it is not an objective-c object.Ransome
CGImageCreateWithImageInRect creates an object and you are responsible for releasing it. If you then create an auto released uiimage from it, you are ok to(and should) release the cgimageref(like you are).Torino
Ultimately it doesn't matter what the retain count of your CGImageRef is-- you created it, therefore you must release it. If the UIImage wants to keep it around, then it's UIImage's responsibility to do so by retaining or copying it, not yours. For example, UIImage might copy the item, meaning that your instance will leak of you don't release it.Emiliaemiliaromagna
@Torino and @Jim, thank you, I am beginning to understand it now.Ransome
N
15

Do Not Call -retainCount

The number returned by retain count is the absolute retain count of the object. There may be many retains in play that you have no control over as they are implementation details of the frameworks. There are always better ways to validate your code's memory management.

Retain counts should only be considered as deltas; if you cause a retain count to increase, you must cause it to be decreased.

Given:

UIImage* getSubImage(UIImage* uim, CGRect rc){
  CGImageRef imref  = CGImageCreateWithImageInRect(uim.CGImage, rc); 
  UIImage*   sub = [UIImage imageWithCGImage:imref];
  CGImageRelease(imref);
    NSLog(@"subimage retainCount=%d", [sub  retainCount]); // is 1
  return sub;
}//getSubImage

imageWithCGImage: returns an autoreleased object. The retain count is irrelevant in that imageWithCGImage: might internally be doing any number of things -- caching, etc. -- that might influence the retain count.

get* is an odd pattern to use in Cocoa programming. You just don't see that much.

I'd suggest something like:

- (UIImage *) subImage: (UIImage *) anImage inRect: (CGRect) aRect
{
      CGImageRef imageRef  = CGImageCreateWithImageInRect(anImage.CGImage, aRect); 
      UIImage*   subImage = [UIImage imageWithCGImage: imageRef];
      CGImageRelease(imageRef);
      return subImage;
}
Navarro answered 28/12, 2010 at 18:50 Comment(3)
@Ransome "get" is typically used when the caller of function is responsible for it(releasing etc) so in this context it seems wrong, and would confuse anyone who maintained that code at first glance.Torino
@Torino "get" is the opposite-- it returns something which is owned by the receiver, not the caller. "new" or "copy" or "alloc" return items which are implicitly retained on behalf of the caller. Check the CoreFoundation APIs for examples; CFURLCopyPath() returns a new CFStringRef which the caller must release, but CFArrayGetValueAtIndex() just returns an item which needs no management by the caller.Emiliaemiliaromagna
according to c.crap.ps/3n6N you would use get prefix on functions which pass in variables which could be mutated/modified. I have actually seen many people using it in different ways but most end up with an object that is not autoreleased.Torino
H
6

There is a naming convention for memory management. All Cocoa codes follow this rule:

Basic Memory Management Rules

I strongly recommend you read this document and follow the rule too. The naming rule is part of the Cocoa too.

From the reference documentation:

You own any object you create. You “create” an object using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy).

You must relinquish ownership of objects you own when you’re finished with them. You relinquish ownership of an object by sending it a release message or an autorelease message (autorelease is discussed in more detail in “Autorelease”). In Cocoa terminology, relinquishing ownership of an object is therefore typically referred to as “releasing” an object.

There is similar rule for C functions of Core Foundation framework. However there is no such autorelease-like things. You have to clarify everything on your documentation if you want to make Objective-C object with C functions.

And this might be helpful to you: imageWithCGImage and memory

Heiduc answered 28/12, 2010 at 16:43 Comment(2)
+1 Okay, I "own" the imref object and therefore I call CGImageRelease. I do not "own" the sub object and therefore I do not call release and I may conclude that it is already in the autoReleasePool.Ransome
@Ransome exactly. :) Once you really internalize these simple rules of ownership, memory management becomes a lot easier.Gnathic
S
4

SubIm is enrolled in the AutoRelease Pool because this object is in fact created by the class method imageWithCGImage and that the rule is that instances created by class methods should always return autoreleased instances.

The code is correct though I don't understand why your are using the C syntax instead of the Obj-C syntax to define your function

Sermon answered 28/12, 2010 at 13:27 Comment(6)
Thank you for that rule, it will help! I still wonder if sub has "CGImageRetained" imref... The documentation only says that imref is not cached...Ransome
I thing it has retained it and your are supposed to release it. I have no idea why retainCount is still 1 after CGImageRelease and it might be that CGImageRelease add to release pool.. I don't know ! Check before and after the release maybe ?Sermon
Well, it is the sub-image's retainCount and it should be 1 if it is in the AutoRelease pool. I do not know how to check the retainCount of imref because it is not an objective-c object.Ransome
CGImageCreateWithImageInRect creates an object and you are responsible for releasing it. If you then create an auto released uiimage from it, you are ok to(and should) release the cgimageref(like you are).Torino
Ultimately it doesn't matter what the retain count of your CGImageRef is-- you created it, therefore you must release it. If the UIImage wants to keep it around, then it's UIImage's responsibility to do so by retaining or copying it, not yours. For example, UIImage might copy the item, meaning that your instance will leak of you don't release it.Emiliaemiliaromagna
@Torino and @Jim, thank you, I am beginning to understand it now.Ransome
T
0
  CGImageRelease(imref);
    NSLog(@"subimage retainCount=%d", [sub  retainCount]); // is 1
  return sub;

Because your method doesn't have the keyword alloc, retain, copy..., you have to autorelease the UIImage before returning it. But the factory method of the UIImage already did that for you. Beware that even you autorelease the sub, the retain count is still 1

Tryck answered 28/12, 2010 at 13:28 Comment(1)
The method that sends the alloc message needs to release or autorelease the object. In the case of the factory method, that's the method that sent alloc, so that's the method that sends autorelease. That said, don't worry about other classes' implementations—your own responsibilities are clearly specified, and all you need to do is follow them.Panacea

© 2022 - 2024 — McMap. All rights reserved.