Memory Management Basics - Objective C - iphone

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 the 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.

Related

How to declare array of pointers in objective c

I dont know how to declare a array which just stores pointers to objects. As per My understanding ,if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to object wont get reflected to original object.
What I want is that create a array of pointers which would just point to objects and changes made to objects would persist. pardon me, If I am missing something basic.
An NSArray or NSMutableArray is an array of pointers. No copying is done.
you have your basics wrong. technically when you do that you create the array of pointers to those objects.
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html read the description.
If you want to get the object copied you have to explicitly say so.
Look at this question for example
Deep copying an NSArray
By the way you should use an NSMutableArray.
Also look at the superclass NSArray
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/cl/NSArray
specifically for the initWithArray:copyItems:
flag
If YES, each object in array receives a copyWithZone: message to
create a copy of the object—objects must conform to the NSCopying
protocol. In a managed memory environment, this is instead of the
retain message the object would otherwise receive. The object copy is
then added to the returned array.
If NO, then in a managed memory environment each object in array simply receives a retain message when it is added to the returned
array.
By default adding an object to a nsmutablearray increases its capacity if necessary, adds a retain for the object, and the pointer to the object.
...if I use method
[ someArray addObject:someObject ] ,
It would then add the copy of object to array and any changes to
object wont get reflected to original object.
While it technically doesn't pertain to the question, I simply must correct your terminology. "Copy" in Objective-C implies that the method -copy is sent to the object, which would create a new object in of itself. What Arrays do is send -retain to their objects, which means that the array itself now owns a stake in the object, which is why changes that don't reference the array (-objectAtIndex:), or have a valid claim to the object itself are not reflected.
What I want is that create a array of pointers which would just point
to objects and changes made to objects would persist. pardon me, If I
am missing something basic.
Well, unfortunately iOS does not support the class NSPointerArray, which would make your life significantly easy in regards to an actual array of pointers. Without getting into any C-craziness, I can only reiterate what I mentioned above: If you need to mutate an object in an array, just access it with a valid reference to it, or use -objectAtIndex. So long as you still have a valid claim on the object (a reference in this case, it's pointer didn't change because it was sent -retain) you can change it. Note the simple example below:
NSMutableString *str = [[NSMutableString alloc]initWithString:#"Hello"];
NSArray *arr = [[NSArray alloc]initWithObjects:str, nil];
NSLog(#"%#",arr);
[str appendString:#" Friend!"];
NSLog(#"%#",arr);
This prints:
2012-08-07 21:37:46.368 .MyApp[2325:303] (
Hello
)
2012-08-07 21:37:46.369 .MyApp[2325:303] (
"Hello Friend!"
)
Simple!

How do memory management properties affect cells of an array?

In my iPhone development book, I'm seeing some strange coding examples in regard to what an array does when objects are added to the array and when the whole array is released. One code example has the following properties on an instance array:
#property (nonatomic, retain) NSMutableArray* myArray;
The author adds an object to the array and, immediately after, releases his pointer to the object. Won't the array cell now point to garbage data? Unless, behind the scenes, the array cell retains the object when added.
SomeObject* someObject = [[SomeObject alloc] init];
[self.myArray addObject:someObject];
[someObject release];
The author also releases the the pointer to the array without first going through each array cell and releasing the individual objects. This is a memory leak unless, behind the scenes, each cell is sent a release message;.
- (void)viewDidUnload {
self.myArray = nil;
[super viewDidUnload];
}
Unless, behind the scenes, the array cell retains the object when added.
Yes, this happens.
... unless, behind the scenes, each cell is sent a release message.
This also happens.
You have answered your own question.
Here is a quote from Collections Programming Topics:
And when you add an object to an
NSMutableArray object, the object
isn’t copied, (unless you pass YES as
the argument to
initWithArray:copyItems:). Rather, an
object is added directly to an array.
In a managed memory environment, an
object receives a retain message when
it’s added; in a garbage collected
environment, it is strongly
referenced. When an array is
deallocated in a managed memory
environment, each element is sent a
release message.
Unlike in C or C++ where you constantly worry about whether to delete an object or not for the fear of it is still being used somewhere else, Objective-C (or rather it's actually Cocoa SDK) uses the mechanism of reference counting or ownership.
You might already know how it works but you need to also know that in Cocoa, if an object A needs to use another object B it should own (i.e. retain) it. That object A should not rely on some other object C already retained B, because it cannot know when C releases it. So in your case, since NSArray needs to use all objects added to it latter during its lifetime, it needs to retain all the objects. And because of that, when the array is de-alloc-ed, it needs to release them.
This concept of "you need to retain what you want to use latter" is very important when you are dealing of lots of objects.
There are several places in apple development guides that explain that is a good practice to take the ownership of an object (send a retain message) if you plan to use it later. You should do it so that the object is not destroyed while you still might need to access it.
Considering that, you were right assuming that the NSArray retains the object when it is added to the collection, as it still might try to access it afterwards.
You can check the Memory Management Programming Guide
When you add an object to a collection such as an array, dictionary, or set, the collection takes ownership of it.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW3
or the Collections Programming Topics for more details
... In a managed memory environment, an object receives a retain message when it’s added.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Arrays.html#//apple_ref/doc/uid/20000132-SW1
You're right on the first point. When an object is added to an array, the array retains the object. Thus, for an object that has been previously retained, it is necessary to release it after adding it to the array or you can end up with a memory leak.
Likewise, when an object is removed
from an array, the array releases the
object. So, if you want to keep it,
you'll need to retain it.
When an array is released, as you
surmised, the array will release all
the objects it contains. Thus,
releasing each object individually is
not necessary and, in fact, would
raise an exception.
Finally, regarding the line of code
in -viewDidUnload that you quoted:
self.myArray = nil;
This works properly with regard to memory management as long as the myArray property was synthesized as follows:
#synthesize myArray;
Synthesizing creates a setter that effectively does the following:
- (void)setMyArray(NSMutableArray *)anArray
{
if (![myArray isEqual:anArray]) {
[myArray release];
myArray = anArray;
[myArray retain];
}
}
So, when called, the above setter will first release the old array (as long as it's not the same object as the new array.) Then, it will retain the new array, which in this case is nil. Note that retaining nil will just do nothing, and won't trigger an error.
Of course, if you don't synthesize the myArray property, or if you override the setter, you will have memory problems unless you also release the old value & retain the new in your setter.

removeAllObjects not removing if action is fast

I am using a search bar in my app and display some results below from an ext API.
The contents first get stored in an array "xyz" and each time the user types in the search bar, I removeAllObjects and reload the table.
The results are correct if the user types slow i.e. [xyz removeAllObjects] works fine...However if the user types very fast, [xyz removeAllObjects] does not seem to have any effect and duplicate items get appended to the array..
I am not sure how to fix this. Please help me. Thank you.
removeAllObjects is an entirely serial operation; that method doesn't return until the array is empty.
Therefore, there must be a thread in play and you are quite likely accessing a mutable array from multiple threads. Mutable arrays aren't thread safe. Fix the exclusivity and you'll fix your problem.
The easiest way is to separate the array being displayed from the array being computed. As soon as the computation is done, move the computed array to the display array and never mutate it again.
Why not create a new NSArray, point the results at that, and then release the old array. That way having duplicates will be impossible. Something like:
NSArray *newArray = [someObject newSearchResults];
NSArray *oldArray = xyz;
xyz = [newArray retain];
[oldArray release];

iphone - will the retain property propagate on an array?

I have a mutable array that has been retained.
This array contain dictionaries with lots of keys. Each dictionary contains objects.
Do I have to retain the dictionaries before adding them to the mutable array or will the array itself retain everything that is added to it (because it is already retained), including the sub objects of its objects in the hierarchy?
thanks.
A Foundation container, like NSArray or NSDictionary, retains the objects it directly owns, but not subobjects owned by the objects.
For example, if NSArray*a contains NSArray*b and it in turn contains NSArray*c, a retains b and b retains c but a doesn't retain c.
That said, your are thinking from a wrong perspective. It's not correct for you to wonder such as "do I have to retain this object (say x) before passing x to another object y, because y might not retain it appropriately?" The point of retain/release is that to make sure an object retains and releases objects it owns. You trust other objects to do the same.
Then, all you have to make sure if you put an object x to an array y, is for you not to release x (if it's not autoreleased) once it becomes unnecessary to you. If y needs it, y retains it, so you don't have to care about it.
Say you have a pre-existing NSMutableArray*array. Then you would do in a method something like this:
NSMutableDictionary* dictionary=[[NSMutableDictionary alloc] init];
... do something with dictionary ...
[array addObject:dictionary];
[dictionary release];
You see, it's the array's responsibility to retain the dictionary, if that array needs it. It needs it, and so it retains it. You don't have to care about that.
The method's responsibility is to retain the dictionary if the method needs it, to release it if the method no longer needs it. So, as shown above, the method releases it once it's done with it by adding it to the array.
Again: the whole point of retain/release is to allow you to consider the life cycle of an object very locally in the code.
Whenever you call a method method:of another object a by passing an object b, you don't have to worry as you do now whether method: retains b or not, and you don't have to worry if you need to retain b before passing b to method:.
It is because every method in the Cocoa framework, and every method you write, retain the object b passed to it if the method needs it later, and don't retain b if it doesn't need it later.
Objective-C containers (such as NSMutableArray) will retain the objects added to them.
This does not, however, have anything to do with anything being "propagated" -- whether or not you call -retain on the NSMutableArray is irrelevant. The NSMutableArray will simply retain objects added to it, and if those objects are themselves some kind of container (such as a dictionary), the sub-objects will themselves already have retained anything added to them, and so forth.
ps. there isn't really a "retain property", there's an (internal) "retain count" on each object. For example, if you create an NSString and add it to 3 NSMutableArray's, each of those arrays will retain it.

How to store object + string pairs without retaining the objects, and what kind of storage to use?

I am implementing a class that has to store arbitrary objects together with a string. i.e.
myUIViewObject, #"that's a nice view"
myUIViewController, #"not really special"
myOtherObject, #"very important one"
this list can be extended and modified at any time, so I thought about using NSMutableDictionary here. But I am not really sure...
The object should be the key, i.e. I want to find easily the matching string for myUIViewController or myOtherObject when I ask for it like so:
- (NSString*)checkObjNoteStringForObject:(id)anyObjectInList;
The other problem is, that when an object gets added to that "list", I don't want it to be retained because of that. NSMutableDictionary retains it's contents, right? Could I just send a -release afterwards to undo this unwanted behaviour, and when removing from the list just sending -retain before doing so? Or is there a more elegant way?
What do you suggest? Thanks # all!
If your dictionary key is not retained, once it is deallocated accesses to the dictionary will lead to undefined behaviour (in practice, they'll crash if a lookup happens to hit that dictionary element). To do what you want, you need a strategy to remove the objects from the dictionary when necessary.
If you do have one – for instance, overriding the objects’ -dealloc and removing them from there – you can do what you want using +[NSValue valueWithNonretainedObject:]. The NSValue will refer to your object without retaining it, and the dictionary will copy the NSValue (keys are copied, not retained). Just remember to create an NSValue for each time you want to look something up in the dictionary; a helper function or method is a good idea.