Appropriate way to set property for NSMutableArray - iphone

I want a mutable array and set it by a property, which alternate is preferable and please explain the reason. I have the 2 alternatives
NSMutableArray *arrSubTitles_temp = [[NSMutableArray alloc] initWithObjects:
#"String 1",
#"String 2",nil];
self.arrSubTitles = arrSubTitles_temp;
[arrSubTitles_temp release];
OR
NSArray *arrSubTitles_temp = [[NSArray alloc] initWithObjects:
#"String 1",
#"String 2",nil];
self.arrSubTitles = [arrSubTitles_temp mutableCopy];
[arrSubTitles_temp release];
I guess the 1st one is better, as self.arrSubTitles property is retained in .h, so (in 2nd) mutableCopy method will provide the mutable array, but also the retain count will be 2 in this case.
Which method is preferable, should I use retain in .h while setting the property or just assign. Please suggest.
Many thanks in advance :)

Use retain as your property attribute, unless you have a good reason not to (ex: retain would cause a retain cycle, it's a primitive, you need to guarantee the object is immutable, etc)
Use the first example you posted, since the second one would leak memory.
If you use assign as your property attribute, then the first option would probably crash:
Allocate an array (owned by you)
Assigned to a property (still owned by you)
Release (no owners, promptly deallocated)
Attempt to access property value: crash (because the pointer is stale)
The second one, however, would work:
Allocate an array (+1 owners)
Copy the array (original: +1 owners, copy: +1 owners)
Assign the copy into the property (both still have +1 owners)
Release the original array (it gets deallocated, the copy continues to exist)
Regardless, it is rather unconventional to have an assign property and then manage the retention of the value yourself. It is extremely error-prone, and much simpler to declare the property as retain and let the generated setter take care of the release/retain dance for you.

Either way is problematic because the caller must provide a mutable array. If the caller ever passes an immutable array (as anything that uses KVC to set the property would be able to do without a compiler warning), you will find yourself holding an immutable array in a mutable-array property, and will shortly thereafter try to mutate it, causing an exception.
What I do is hold a mutable array, which I create in init and release in dealloc, as the value of the property, declare the property as #property(nonatomic, copy) NSArray *myArray, and implement setMyArray: to send a setArray: message to my mutable array. At no point do I ever switch out the array; I exclusively and privately own the same mutable array for the entire lifetime of my object.
An equivalent implementation of setMyArray: would release the array and set the myArray variable to a mutableCopy of the input array. The only difference is that this would create and throw away more arrays over the duration of the process.
With either of these solutions, the caller does not need to worry about whether it passes a mutable or immutable array; the property will always do the right thing. And the caller would, as usual, not need to retain it on my behalf.
Note that a setter implementation generated by #synthesize will not do the right thing. It sends copy to the input array, which will return an immutable copy. This would be even worse, as then it would not work even if the caller does pass in a mutable array. You must implement a custom setMyArray: accessor to correctly handle both immutable and mutable arrays.

What about the setting it as autorelease
NSMutableArray *arrSubTitles_temp = [[[NSMutableArray alloc] initWithObjects:
#"String 1",
#"String 2",nil] autorelease];
self.arrSubTitles = arrSubTitles_temp;
[arrSubTitles_temp release];

Related

How does calling retain in different ways on the same object work?

I want to know difference between following two:
NSMutableArray *myArray = [[someObject returnMutableArray] retain];
and
NSMutableArray *myArray = [someObject returnMutableArray];
[myArray retain];
I have some complex data flow project in which I have to return different NSMutableArrays.
I also want to know what is the best way to return NSMutableArrays.
There is no difference apart from how you want your code formatted.
The reason this works is in the documentation for NSObject
As a convenience, retain returns self because it may be used in nested expressions.
Therefore retain returns the object that is was called upon (a mutable array in your case) which means you can nest methods or simply use the return value in the assignment as you have in your example.
For returning values from methods you should make sure it is autorelease'd
So using your snippet
- (NSMutableArray *)myMethod;
{
NSMutableArray *myArray = [someObject returnMutableArray];
// ... do some work
return myArray;
}
Because the method returnMutableArray does not contain the keyword's alloc, new or copy it should be made to return an autorelease'd object. Now as we are returning the object at the end of the method we don't need to take any further retains as it will be ready for returning.
If you are unsure on memory management you should read the Advanced Memory Management Guide or (preferably) start using ARC
There is no difference between 1 and 2.
The best practice on iOS is to return autoreleased objects from methods, and there should be no need to retain them.
If you are confused about how retain and release work, you should probably just enable ARC in your project, which manages retain and release for you automatically.

Autoreleasing object returned from NSMutableArray

When a method returns an object that is taken from and NSMutableArray does the object must be autoreleased? Check the following method. Also should I autorelease the nil value?
-(NSObject*)getElementByID:(NSString*)ID{
for(int i=0;i<[elements count];i++){
NSObject *element = (NSObject*) [elements objectAtIndex:i];
if([element.key isEqualToString:ID]){
return [element autorelease];
}
}
return nil;
}
You must not autorelease element because you are not an owner of it (you have not put a retain on it). You would have become an owner of it if you acquired it using alloc, new or retain. Since you acquired this object calling objectAtIndex:, you do not own it. See Three Magic Words. Calling autorelease here will cause a crash later do to over-release.
Your method name is incorrect and breaks KVC. A method that begins with get must take a pointer that will be updated with the result. This should be elementForID:. As noted above with the three magic words, naming in ObjC is very important to writing stable code
As a side note, it is traditional to use id is most cases rather than NSObject*. They mean slightly different things, but typically id is correct.
You never need to do any memory management related things to nil. So, no, you should not send autorelease to nil.
You also should not need to send autorelease to the element object that you are returning from your elements array. That object you are returning will remain in memory by virtue of elements itself having retained it. If the calling method would like to retain the value that you return, it may. But if that calling method only uses the returned value within its own scope, it is safe for it to do so without retaining it.

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.

NSMutableArray object thinks it is an NSArray :(

So I have a property NSMutableArray *grades. At the only place where I set this property, I am doing this:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.grades = array;
[array release];
[self.grades addObject:#"20"];
The last statement generates an exception: -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'.
What in the world am I missing?
It sounds like the property is set to copy, which means the synthesized accessor makes an immutable copy of the array
Make sure grades is a NSMutableArray.
Edit:
copy returns an immutable copy, so you can't make changes. From Apple's Objective-C docs:
Copy
If you use the copy declaration attribute, you specify that a value is copied during assignment. If you synthesize the corresponding accessor, the synthesized method uses the copy method. This is useful for attributes such as string objects where there is a possibility that the new value passed in a setter may be mutable (for example, an instance of NSMutableString) and you want to ensure that your object has its own private immutable copy. For example, if you declare a property as follows:
Although this works well for strings, it may present a problem if the attribute is a collection such as an array or a set. Typically you want such collections to be mutable, but the copy method returns an immutable version of the collection. In this situation, you have to provide your own implementation of the setter method, as illustrated in the following example.
Copying the entire collection on assignment is a heavy operation. Are you sure you don't want to retain the collection, or just assign it?
If you really want a mutable copy, then write your own setter as the docs suggest.
- (void)setGrades:(NSMutableArray *)array {
// make shallow/deep copy here, and assign to `grades`, not `self.grades`
}
What is grades declared as?
From the looks of the error message your declaring grades as an NSArray and while this is valid it does mean that you lose the mutability of the array.
To maintain the array as mutable you'll need to declare grades as an NSMutableArray as well.
edit:
In light of your edit the reason could be that your using the copy keyword in the property, this would mean that when your assigning the array using self.grades the synthesised setter method makes an immutable copy of array
self.grades probably returns an NSArray if declared as #property NSArray* grades seeing this the compiler freaks and does not want to support addObject: method. You have 2 options
cast it
like [(NSMutableArray*)self.grades
addObject:].
add the object before assigning the
array.

insertObject doesn't add object?

I'm trying to grope my way through Obj-C to build an iPhone game.
I'd like to build an array of objects for later use. Here's what I tried:
NSMutableArray *positionIcons;
[positionIcons insertObject:annotation atIndex:0];
positionIcons = [NSArray arrayWithObjects:annotation, nil];
The insertObject line leaves the count at 0. However, the next line correctly inserts the object (and count moves to 1). What gives?
You need to initialize positionIcons, change the code to:
NSMutableArray *positionIcons = [[NSMutableArray alloc] init];
[positionIcons insertObject:annotation atIndex:0];
positionIcons = [NSArray arrayWithObjects:annotation, nil];
#msaeed has the right answer, but it's worth taking a little more time on that code fragment, because it leaks memory.
The iPhone doesn't support garbage collection, so it's important to understand how Objective-C's semi-automatic memory management works. Apple has a good reference here, but the gist of it is that you are responsible for maintaining the "retain count" of your objects. When initializing an object with an -init instance method (such as [[NSMutableArray alloc] init] in your example, but also any other method starting with "init", like [[NSMutableArray alloc] initWithCapacity:42], the newly-initialized object has a retain count of 1. Subsequent calls to that instance's -retain method increment the retain count, while calls to the instance's -release method decrement the count. When the count reaches 0, the object is deallocated, and further attempts to send it messages will result in null pointer exceptions.
In the case of #msaeed's corrected code, here's what's happening, by line:
A new instance of NSMutableArray is allocated; the -init method is called, which initializes the instance and sets the retain count to 1. The positionIcons pointer is then set to the address of this new instance.
The -insertObject:atIndex: method is called on positionIcons, and all is well (also, by convention adding an object to a collection like NSMutableArray increments that object's retain count, the idea being that the collection now has, in some sense, "ownership" of that object, and doesn't want it to be deallocated from underneath it).
A new instance of NSArray is allocated, and the positionIcons pointer is then set to the address of that new instance. Because the retain count of the NSMutableArray from line one is still 1, it will not be deallocated, and since you've lost your reference to it, you can never call -release on it to clear it out of memory. There's your leak.
By convention, there's a difference in how you manage objects that are initialized with -init instance methods versus class methods like +arrayWithObjects: (in the first case, you have to release the object yourself, but in the second case, the object has already been sent an -autorelease message and will be deallocated on the next pass through the program's runloop unless you call -retain on it.