Memory Management Basics - Objective C
I have a general question about objects, memory, and retaining.
I am not a complete newb (I have apps for sale on the iTunes Store) but something must have slipped past me. I work with the Google Docs API, and yesterday I was downloading a spreadsheet feed, and enumerating the results, namely, the Spreadsheet Record Feed.
After enumerating and adding objects to a dictionary, the dictionary got added to an array before the loop went to the next Record. So after 5 times through, the array had 5 objects, one dictionary per record, with values from each. Weirdly, at the end, the Array had 5 copies of t开发者_C百科he same information. Each time through the loop, the dictionary changed, like it was supposed to, but when I inserted it into the array, the other objects in the array changed to match.
I looked through some stuff on StackOverflow and found a suggestion to try this:
[array insertObject:[dictionary copy] atIndex:0];
That fixed it. Adding the copy method made everything work like normal.
I'm just wondering why.
Usually, when an object is put into an array, it's kept intact even if the original object is modified or destroyed.
You are mistaken. Arrays (and all other container classes in Cocoa) never work like that. They only store a reference to the objects they contain (and retain them) so any changes in the original objects will be reflected if you retrieve them from the array (because they are the same objects).
Sure, by calling copy
you are creating a copy of the dictionary so now you are dealing with separate objects. At the same time, you are now leaking memory because you are responsible for releasing an object that is returned from copy
.
Also, bear in mind that copy
only makes a shallow copy, so the actual content of the copied dictionaries is not being copied. If you change the dictionaries' contents, these changes will be reflected in both dictionaries (the "original" one and the copied one you have added to the array).
When you insert an object (let's call it X) into an array, what actually gets placed into the array is a copy of the pointer to X, and not a copy of X itself. X is sent a retain
message so that the array can hold on to it, but X is not sent a copy
message. This means that changes to X later on will affect the object "stored" in the array.
WARNING: The suggested solution results in a substantial memory leak, since the copied data is never released. A better solution is to autorelease the copied array, so that it will be released when the array is released.
[array insertObject:[[dictionary copy] autorelease] atIndex:0];
Or, for iPhone code (in which autorelease can be bad, especially in loops):
NSDictionary * newDict = [dictionary copy];
[array insertObject:newDict atIndex:0];
[newDict release];
UPDATE: Be sure to read Ole Begemann's answer as well. He makes an excellent point about deep vs. shallow copies of the dictionary.
精彩评论