I have an class method which generates a UIImage, like this:
+ (UIImage*)imageWithFileName:(NSString*)imgFile {
UIImage *img = nil;
NSBundle *appBundle = [NSBundle mainBundle];
NSString *resourcePath = [appBundle pathForResource:imgFile ofType:nil];
if (resourcePath != nil) {
NSUR开发者_StackOverflow社区L *imageURL = [NSURL fileURLWithPath:resourcePath];
NSData *data = [[NSData alloc] initWithContentsOfURL:imageURL];
img = [UIImage imageWithData:data]; // should be autoreleased!!
[data release];
}
return img;
}
However, when I use this, the image data is NEVER freed. There is definitely a memory bug with this, although I didn't break any memory management rule I am aware of. My guess is that because this is a class method which gets called from instance methods, There is no active autorelease pool in place or it's one that only gets drained when I quit the app. Could that be right?
Once you've passed the data on to img
, it's out of your hands. It's possible that UIImage keeps the raw data around in its internal implementation. But it doesn't matter. From your perspective, you've appropriately released the data
, and it is entirely up to img
to determine whether it wishes to keep it around.
Do you ever explicitly retain the returned UIImage? Or pass it to another class? If, for example, you place it into a UIView, the view will retain img
until it is no longer needed.
With regards to your explicit question: there is a main run-loop AutoreleasePool. It generally gets drained after every event loop.
UIImage retains the image data, so if you are actually leaking memory you should check if you are freeing the returned img.
The data variable retain count is this:
- allocation: 1
- create an image with data: 2
- [data release]: 1
so until the img is not freed, the provided data it is not released (that makes sense, since the image needs the data)
you can use retaincount to check for the current retain count of an NSObject derived item
The real question I think is, how are you measuring that the memory is not released.
Autorelease pools are all related to the thread and runloop you are in - because they free memory when a call returns all the way to the main runloop. It doesn't matter if you are calling a class method or an instance method or even a C function, autorelease will work the same in all cases.
I know in your testing you found there are differences, but simply put if you see a difference it is for some other reason - because autorelease always works the same across the system as far as freeing memory if you are in the same runloop.
After some tests I have figured out, that the problem is indeed related to Autorelease Pools. If I use the exact same code within an instance method, there is absolutely no problem.
But as soon as I use that in a class method, the autoreleased UIImage object never gets released. Whatever autorelease pool of the autorelease pool stack is the topmost in class methods, it's seriously not the same as in instance methods. I am pretty sure now that class methods receive a very low level autorelease pool which only gets drained or released when the app quits.
So be careful doing autoreleased stuff in class methods. Of course, it doesn't help at all to create an ARP locally, because what you might need (like I do in this case) is to return an autoreleased object.
I will change my code to +newImageWithFileName: and return an not-autoreleased object, so the receiver definitely has to release it or may send -autorelease to that.
I hope someone can provide some more details on this issue.
精彩评论