My app downloads images from a URL in a specific order and the user views them. I have the app preloading the next image and keeping the older images in a cache.
This cache is basically a NSMutableDictionary with keys that are associated with the image ID. Each object in the cache is also an NSDictionary with the url, the image URL, a title, and a NSData object. When I download the image, I add a new dictionary object into the cache with those elements. I store the NSData object like this: NSData *imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageurl开发者_高级运维]]; I then add this object to the child dictionary object.
This obviously takes up a large amount of RAM after a large number of photos are stored and cached, so I respond the the memory warning by clearing the entire cache. The problem is, no matter what I have tried, no RAM is freed. The objects disappear (like they should), but don't give back RAM. I have tried doing the following:
- Looping through the cache dictionary, releasing the data object in the child dictionary, then when all items are looped through, remove all of the cache dictionary child objects.
- Looping through the cache dictionary, releasing the child dictionary, removing all objects when finished
- Even removing all objects, then releasing the entire cache object (then recreating it, because it still needs to be used)
- And other random combos of releasing, removing, etc.
The problem is, NOTHING I have tried has freed RAM. Is there something I am doing wrong with releasing the memory of the NSData objects?
The way memory management works in Objective-C is you gain ownership on an object by alloc/initing, -retaining or -copying it, and you dismiss it when you no longer need it via -release or -autorelease.
What you want to do is:
1) Create the data object
NSData *imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageurl]];
You now own the object imgData points to.
2) Add that object to your child dictionary
[childDictionary setObject:imgData forKey:@"imgData"];
3) Dismiss the data object. You don't need it anymore. childDictionary needs it, but that is not your problem. childDictionary, like every other object, is responsible for managing its own memory and ownerships.
[imgData release];
Another way you could do that is create an autoreleased version of the data object, and add that to the dictioary.
Next you need to insure you're managing childDictionary
's memory, as well as cache
's memory properly. For instance:
1) Create an autoreleased childDictionary
NSMutableDictionary *childDictionary = [NSMutableDictionary dictionary];
2) Add stuff to childDictionary (like we did before)
3) Add childDictionary to the cache
[cache setObject:childDictionary forKey:yourKey];
4) Since childDictionary was created autoreleased, you don't need (and shouldn't) release it now that you don't need it. It will be automatically released by the main autorelease pool at a later time. If you had created your childDictionary using:
NSMutableDictionary *childDictionary = [[NSMutableDictionary alloc] init];
which returns an object with a retainCount
of 1, then you would have needed to dismiss ownership now:
[childDictionary release];
As far as cache goes, you probably want to keep that around for a long time: it is most likely an instance variable of whatever object manages the cache, and it's reset with [cache removeAllObjects]
when you hit the memory warning.
If you develop on the Mac using garbage collection, all of this is useless. But since you're on the phone, you need to know these simple rules about memory management. My explanation really overlooks a lot of the concepts you should be familiar with, so I strongly encourage you to read the Apple documentation about this.
A simplified code sample that demonstrates this leak would be helpful. Just from your description, is it possible that the call you're using to add imgData to the dictionary is retaining it? If you're not then immediately releasing imgData in your own code, you'll have a retain count of 2. When your local imgData pointer goes out of scope, you will have leaked the memory. This is just a stab in the dark without a code sample, though.
精彩评论