Reputation: 4685
My app makes use of lots of scaled images in animations. To avoid skipping frames, I scale my images and save them, before running the animation. Here is my code to save images:
+ (void)saveImage:(UIImage *)image withName:(NSString *)name {
NSData *data = UIImagePNGRepresentation(image);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fullPath = [directory stringByAppendingPathComponent:name];
[fileManager createFileAtPath:fullPath contents:data attributes:nil];
}
Unfortunately, when I call this function repeatedly, I have memory problems. I guess I'm trying to save about 10MB worth of images. I'm thinking that perhaps the problem is with the autoreleased variables--perhaps I should alloc the data, and release at the end. But I can't find an alloc version of UIImagePNGRepresentation
. Can anyone help?
Upvotes: 1
Views: 2857
Reputation: 17012
UIImagePNGRepresentation returns an autoreleased NSData object. In other words, the data allocated will only be dealloced once you get to the release (or drain) call of the nearest enclosing NSAutoreleasePool block.
If you are calling the above code from within a loop, it's possible your code is never getting the chance to autorelease all that memory. In this sort of situation, you can enclose your calls in your own NSAutoreleasePool:
for (int i = 0; i < 10; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self saveImage:someImage withName:@"someName.png"];
[pool drain];
}
N.B. I believe working with PNGs this way is quite slow compared to using JPGs (UIImageJPGRepresentation). Just FYI.
Upvotes: 4
Reputation: 100652
What does the outer loop look like? If it's something like:
for(n = 0; n < 1000; n++)
{
... something ...
[class saveImage:image withName:name];
}
Then leaving things in the autorelease pool could be your problem. The autorelease pool is drained only when the call stack completely unwinds back to the run loop (since otherwise you wouldn't be able to use autoreleased things as return results). Given that you're not releasing anything, you might try modifying your code to:
+ (void)saveImage:(UIImage *)image withName:(NSString *)name {
// create a local autorelease pool; this one will retain objects only
// until we drain it
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSData *data = UIImagePNGRepresentation(image);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fullPath = [directory stringByAppendingPathComponent:name];
[fileManager createFileAtPath:fullPath contents:data attributes:nil];
// drain the pool, which acts like release in reference counted environments
// but also has an effect in garbage collected environments
[localPool drain];
}
So, for each save of the image you create your own autorelease pool. The most recently initialised autorelease pool automatically sets itself to catch all autoreleased objects from then on. In a garbage collected environment like iOS, calling 'drain' on it causes it to be deallocated and all objects it holds instantly to be released.
Upvotes: 1