iphone - will the retain property propagate on an array? - iphone

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.

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!

Can somebody tell me what retain and release exactly do in objective-c?

I have a bit of confusion about retain and release variable.
These are my question:
I have an object A allocated
Object *A = [[Object alloc] init]
Does this increase the retain count? Is it the same as having
Object *A = [[[Object alloc] init] retain].
I know if we call retain, the object will be held until we released it.
2.1.
I have a retained Object A. This Object A was passed to a function as an argument. Later in the function I released it. Will it release the memory of the Object A? if it is, does the Object A no longer existed.
2.2
Follow on with 2.1, instead of releasing Object A. I created a local variable Object *B = A. If I released Object B will it also release Object A. Or if I retain B, will it retain A as well.
I am a bit of confused so I want to ask anyone here to directing me to the right path.
Thanks
Object *A = [[Object alloc] init] Does this increase the retain count?
Yes, more specifically, likely, it initializes the retain count to 1.
Is it the same as having Object *A = [[[Object alloc] init] retain]. I know if we call retain, the object will be held until we released it.
Not the same. That will increase the retain count twice. There's not much reason to increase the retain count twice in the same place. If you did do that, you'd be responsible for calling release twice.
I have a retained object A. This Object A was passed to a function as an argument. Later in the function I released it. Will it release the memory of the Object A? if it is, does the object A no longer existed.
First, you should make sure that you understand that whether release frees memory or not depends on if there are other owners that are holding a reference to the object. If more than one thing has retained the object, then release will not free the memory in that case.
Second, bad idea. To keep sanity with reference counting, you should follow certain patterns.
retain (or alloc) instance variables, release them in dealloc or before.
retain (or alloc) local variables if needed, and release them before you exit the method.
The caller does not own the return value of a function. That implies that you handle a function return value like a local variable, but autorelease it before you return it instead of releasing it. That ensures it will last at least long enough for the caller to retain it, if needed.
One of the patterns is not for a method caller to own a reference to a method argument, but when the function returns the caller does not own the reference. The method should not release the caller's reference.
Follow on with 2.1, instead of releasing Object A. I created a local variable Object *B = ObjectA. If I released B will it also release Object A. Or if I retain B, will it retain A as well.
retain and release are sent to objects, not references. A and B refer to the same object, so it's the same calling retain or release on one as on the other.
It is legitimate, but probably unnecessary, to [B retain] and later in the same method [B release]. But do not do [B release] only, thereby expropriating A's ownership of the object.
Remember this: NARC.
New, Alloc, Retain, Copy are the 4 commands that will increase the retain count. In other words, for every NARC, you need a corresponding release to release that memory.
Calling alloc (or new) increases the retain count. If you retain again after that, that increases it once again. There is no need to do that second retain except for specific wanted cases.
The main rule is : the one that alloc is the one that release. Of course you can bypass that if you reaaly know what you are doing.
I have a retained object A. This Object A was passed to a function as
an argument. Later in the function I released it. Will it release the
memory of the Object A?
Basically, Yes.
if it is, does the object A no longer existed.
Yes again, but sometimes, it remains "in memory" so calls to it may work for a very very short time. Very short.
Follow on with 2.1, instead of releasing Object A. I created a local
variable Object *B = ObjectA. If I released B will it also release
Object A. Or if I retain B, will it retain A as well.
if you retain B, then the object references by A is retained twice. so releasing B does not free the object referenced by A. But if you don't retain B, then releasing B equals to releasing A (only one retain count).
Tip
Imagine that any reference you declare (Balloon* rope) is a rope you get in your hand to retain a Balloon object that contain things. The rope is not the object. The object (the balloon) is created somewhere in memory (here in the space). If you retain a second time, you get another rope to that balloon and get it in the other hand. To free the balloon, you need to free both ropes.
Note that alloc (or new) creates a rope so the balloon you've just created does not go immediatly in outerspace.
Is that clear ?
1) init is not the same as retain.
The init method actually puts the object in a correct "initialization" state so that the object can be used by calling the parent classes init method, as well as setting up any additional setup that is placed in the init method implemented in the objects .m file. The retain actually just increments the retain count by 1.
2) 2.1) When you allocate memory for an object, you want to release it in the same scope per say, so when you pass in an object into a method you wouldn't release it there. However, if you did release it in the function/method where it was passed in since you are passing in an object reference, it will be released. You can't be entirely sure when the object itself will cease to exist though.
2.2) When you say Object *B = ObjectA you aren't actually creating another object, but are creating a new reference or pointer to ObjectA. If you call release/retain on the reference B, since it's pointing to the same object as ObjectA, it's the same thing as calling release/retain on ObjectA.

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.

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.

setting objects to nil when releasing NSArray

When I get a memory warning I am releasing a bunch of objects stored in an NSMutableArray.
[_children release];
I also need to recurse through objects at some point (potentially after a mem warning has happened), so I need to check if the objects are still around, which I do with comparison to nil- which isn't going to work because releasing the array doesn't nil anything.
I can't loop through the array removing objects 1-by-1 and setting to nil because I'm not allowed to modify an array during iteration.
It feels like I'm missing something obvious. I just want to release + nil some values in an array.
Sorry, but what you are doing seems pointless to me. You cannot set an object to nil, you can only set the reference to an object to nil, but that has on influence on other references.
NSObject * a = [[NSObject alloc] init];
NSObject * b = a;
[a release];
a = nil;
// b is NOT nil! b still points to the memory location where
// a used to be, which is now not valid anymore and using b
// for anything may crash your application!
If I put "a" into an array and then remove it again from the array, the retain count of "a" is decreased by one. Either it is then still bigger than zero, in which case "a" will not be released or it is zero, in which case it is released. Setting the reference to a to nil after it was released has no influence on other variables still pointing to "a".
So even if NSArray was setting the reference to "a" to nil after removing it to the array and releasing it (because its ref count got zero), it will have no affect on an instance variable still pointing to "a".
I'm not really sure what you are trying to do and it is certainly possible, but you are on a totally wrong track here.
Assuming that you aren't retaining the array elements elsewhere, surely by removing an object from the array, or releasing the array itself you are also releasing the element(s). I think that is as much as you need to do.
From the Apple documentation:
each object receives a retain message
before its id is added to the array
and a release message when it is
removed from the array or when the
array is deallocated