Reputation: 120354
I ran my app in Instruments with the VM Tracker a discovered a growing Image IO memory consumption.
Effectively, the app did quite a bit of reading images from disk with initWithContentsOfFile:
. I read once that this method was the spawn of Satan, so I replaced it with the following:
NSData *data = [NSData dataWithContentsOfFile:path];
UIImage *image = [UIImage imageWithData:data];
This reduced virtual memory greatly (about 60%), as shown below:
But, why does the Image IO virtual memory keep growing over time, when there aren't any leaks and my app is just using 15MB of live memory?
Is there something I can do to make sure that this Image IO memory is released?
Basically, the image reading from disk is done like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
NSData *data = [NSData dataWithContentsOfFile:path];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
});
I also tried the following without any significant changes:
[NSData dataWithContentsOfFile:path options:NSDataReadingUncached error:nil]
insteadUIImage *image = [UIImage imageWithData:data];
to the main queueWhich makes me think the problem might be elsewhere.
Upvotes: 4
Views: 3143
Reputation: 120354
Thanks to Heapshot Analysis I found that the images were retained by a NSCache
.
The problem was that the NSCache
didn't release any objects on memory warning. The solution: make it observe UIApplicationDidReceiveMemoryWarningNotification
and remove all of its objects when this happens. This is how Instruments looks now:
Instruments is awesome. Thanks to @NikolaiRuhe and @nielsbot for making me dig deeper.
Also, I should add that memory consumption lowered when using NSData
because dataWithContentsOfFile
doesn't account for retina files (d'uh). So imageWithContentsOfFile:
might still be the spawn of Satan, but this one wasn't its fault.
Upvotes: 1
Reputation: 81878
You should at least wrap the background processing into an autorelease pool:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^(void) {
@autoreleasepool {
NSData *data = [NSData dataWithContentsOfFile:path];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
}
});
This way you make sure any autoreleased objects on the background thread go away as fast as possible.
Upvotes: 2