Objective C when to Alloc and Init Clarification - objective-c

I am looking for some clarification on initializing variables in Objective C.
Say I have a method that returns an array.
-(NSMutableArray *) getArray
{
NSMutableArray *arr = [[NSMutableArray alloc]init]; //line A
for(int i = 0; i < 10; i++)
{
[arr addObject:#"word"];
}
return arr;
}
And then I call this method.
NSMutableArray *myArray = [[NSMutableArray alloc]init]; //line B
myArray = [self getArray];
So should I be allocating memory in both lines A and B, neither, or in just A or just B? The alternative being simply
NSMutableArray *arr; //replacing line A
NSMutableArray *myArray; //replacing line B

For any one array, you should be allocating its memory and initializing it once.
To start with, that means that your alternative at the end doesn't work. It's declaring that these two variables exist and will point to arrays but does nothing to create and assign them.
Line B creates and initializes an array just fine, but then immediately loses its only reference to it by assigning the result of getArray to the same variable. If you use ARC memory management, that's a bit wasteful; without ARC, it's a memory leak.
Line A also creates and initializes an array correctly and, as far as the code you've posted goes, that's the one that gets affected by whatever you do next to myArray.
(Remember that the things you declare as variables -- like NSMutableArray *arr -- can be thought of as names for the actual objects rather than objects themselves.)

You should allocate memory in A, but in B.
The alloc/init in A creates the object that the variable arr is refering to. You pass that object as the return value of method getArray.
In B, you simply want myArray to refer to the returned object, you don't want to have a new object. If you do:
NSMutableArray *myArray = [[NSMutableArray alloc]init]; //line B
myArray = [self getArray];
the first line creates a new object and assigns that to myArray. The second line replaces that value of myArray immediately with another object that is returned from the method. Without ARC (automated reference counting), this will lead to a memory leak of the object created in the first line.

Related

correct way to allocate the NSMutableArray

I wanted to know which is the right way to allocate an NSMutableArray.
NSMutableArray *a;
Where a is a class level variable.
First method is:
self.a = [NSMutableArray alloc] init];
Second method is:
a = [NSMutableArray alloc] init];
Which method is better? Can anyone please help me out in this?
If a is a class variable, then correct way to allocate NSMutableArray will be creating a temporary array and assigning it to class variable, followed by releasing the temporary variable.
You can do this way:
NSMutableArray *temp = [[NSMutableArray alloc]init];
self.a = temp;
[temp release];
It depends on the property type. (Though it's in most cases a retain)
You should either use a temp value or create it in one string and send an autorelease message:
self.a = [[NSMutableArray alloc] init] autorelease];
You must send an autorelease becuase a property increases retain count by one. This is the same as doing:
[self setA:array];//where array - is newly created array
Where:
- (void)setA:(NSMutableArray *)array {
if (array != a) {
[a release];
a = [array retain];//here you increased a retain count by 1
}
}
You can also use an autorelease method of creation:
self.a = [NSMutableArray array];
There are several ways. But below way is good enough per me whether you are working with ARC/Non-ARC. Just make sure you have created property.
self.a = [NSMutableArray array];//It will return autorelease object.
The difference between the methods:
1) When you use self.a ,
You use the setter & getter methods created in the #synthesize.
2) When you use just a,
You bypass the accessor methods and directly modify the instance variable. (a in here).
There are two ways to look at it.
Many programmers say that you should never call the accessors from within the implementation of the object as this adds unnecessary overhead.
Some others say that you should always use the accessors, and never access the instance variable directly.
It is generally safe to use an object directly, if you are reading its value only. If you are modifying the object, you should use the accessors in order to make sure that any other objects observing that property are properly notified.
The latest objective C syntax allows you to create mutable and non-mutable arrays very quickly.
The following two examples demonstrate this:
NSArray *objectsToAdd = [#"Ted", #"Ned" , #"Sed"];
NSMutableArray *objectsToAdd = [ #[#"Ted", #"Ned" , #"Sed"] mutableCopy ];
NSMutableArray *a = [[NSMutableArray alloc] init];

NSMutableArrays - can I do this?

In my app, the singleton class (SharedData) allocates memory for a NSMutableArray:
[self sharedMutableArray] = [[NSMutableArray alloc] init];
Class A populates the this sharedMutableArray:
NSObject *obj = [NSObject alloc] init];
[sharedMutableArray addObject];
obj = nil;
Class B does this - and that's my question:
NSMutableArray *tmpArray = sharedMutableArray;
... uses the tmpArray locally
[tmpArray removeAllObjects];
tmpArray = nil;
This is an inherited code and my hunch is that this is a NO-NO. Can some one confirm that assigning nil to tmpArray will release memory for sharedMutableArray also.... I guess the author wanted to release tmpArray only...
Assigning nil to tmpArray only sets your pointer to the object to nil. It does not affect the object itself (or its lifecycle) at all. In this case, setting the objects you've created to nil does nothing, since their variable declaration is in local scope - if you want the objects to be deallocated from memory you need to send them release before setting the pointer to the object to nil.
However, sending removeAllObjects is affecting your original sharedArray, because you didn't copy the array, you simply set a new pointer to point to the 'singleton'. You probably want this:
NSMutableArray *tmpArray = [NSMutableArray arrayWithArray:sharedMutableArray];
You won't need to use removeAllObjects in the above case because it will be autorelease'd. I suggest you read this.
tmpArray is a pointer, and it's initialized to point to the same mutable array that sharedMutableArray points to. For that reason, the line:
[tmpArray removeAllObjects];
will empty out the array, and anyone using sharedMutableArray will see that change. In other words, the assignment
NSMutableArray *tmpArray = sharedMutableArray;
doesn't make a copy of the array itself, it only copies the pointer. Any messages you send using that pointer will go to the shared array. Likewise, assigning nil to tmpArray sets the pointer tmpArray, but doesn't do anything to the array itself.
Finally, setting a variable to nil never releases memory. Setting a property to nil, on the other hand, will release memory under some conditions (e.g. when the property is declared to retain its contents). You're setting a variable here, not a property, so there's no chance that the array will be released.

copy objects from one NSMutableArray to another NSMutableArray

I am trying to understand copying objects from one NSMutableArray to another. Consider the following 2 scenarios:
1 - copying original to clone where changes in the clone will affect the original.
2 - copying original to clone where the changes in the close will NOT affect the original.
First, I am trying to produce scenario #1 first with the following code. From what I understand, when copying array not using 'mutablecopy', the clone array will just hold the pointer to the same string objects in the original. So if I were to change the first element of the clone to a different object, the first element of the original would change too right? ... but that's not the result I am getting. Why?
Matter of fact, when I use mutablecopy
[self.cloneArray addObject:[[self.originalArray objectAtIndex:i] mutableCopy]];
I get the same result. I am confused.
ArrayClass.h
#interface ArrayClass : NSObject {
NSMutableArray *_originalArray;
NSMutableArray *_cloneArray;
}
#property (nonatomic, retain) NSMutableArray *originalArray;
#property (nonatomic, retain) NSMutableArray *cloneArray;
ArrayClass.m
#synthesize originalArray = _originalArray;
#synthesize cloneArray = _cloneArray;
_originalArray = [[NSMutableArray alloc] initWithObjects: #"one", #"two", #"three", #"four", #"five", nil];
_cloneArray = [[NSMutableArray alloc] initWithCapacity:[self.originalArray count]];
for (int i=0; i<5; i++) {
[self.cloneArray addObject:[self.originalArray objectAtIndex:i]];
}
// make change to the first element of the clone array
[self.cloneArray replaceObjectAtIndex:0 withObject:#"blah"];
for (int n=0; n<5; n++) {
NSLog(#"Original:%# --- Clone:%#", [self.originalArray objectAtIndex:n], [self.cloneArray objectAtIndex:n]);
}
...
2011-03-27 03:23:16.637 StringTest[1751:207] Original:one --- Clone:blah
2011-03-27 03:23:16.638 StringTest[1751:207] Original:two --- Clone:two
2011-03-27 03:23:16.639 StringTest[1751:207] Original:three --- Clone:three
2011-03-27 03:23:16.642 StringTest[1751:207] Original:four --- Clone:four
2011-03-27 03:23:16.643 StringTest[1751:207] Original:five --- Clone:five
You are thinking about this way too hard.
In Objective-C, you have references to objects. An NSString *foo; simply defines a variable foo that refers to an NSString. If you say NSString *bar = foo;, then bar will have a reference to whatever object foo was referring to. No more, no less.
An NSArray is just a collection of object references. So, if you say:
NSArray *b = [NSArray arrayWithArray: a];
You are creating an array b that contains all of the same references to the exact same set of objects as a. If you modify an object referred to by a, that'll be the exact same object in b and the modification will be reflected.
When you copy an object, you are creating a new object that has the identical internal state as the original. I.e. when you say NSMutableString *foo = [barString mutableCopy];, then foo is a reference to a new string; a different one than barString.
So... when creating a new array, the question is do you want the array to contain the exact same contents as the original array or do you want it to contain a new set of objects that you can modify?
You have a misunderstanding of what's going on. The replaceObjectAtIndex:withObject: call isn't modifying objects in the array, it's modifying the array itself. After this line:
[self.cloneArray replaceObjectAtIndex:0 withObject:#"blah"];
you've replaced the object in your clone array, but you haven't changed the original array at all. If you actually modified the NSString object you put in the arrays, you might be able to get the behaviour you were expecting. You won't be able to do it with the objects you've put into the original array in your example, though, since they're immutable string objects. If you stuck mutable strings in there, used the same loop to 'clone' your array, and then did something along the lines of:
[[self.cloneArray objectAtIndex:0] appendString:#"some junk to append"];
you would actually modify the string object at index 0. Since both arrays still contain that same object, you'd get the 'modify original array by changing the objects in the clone array' behaviour.

My NSMutableArray doesn't work

sorry for my stupid question (beginner)
I got the demo program Accelerometergraph the apple site and would like to use
NSMutableArray in the values of acceleration x.
but my NSMutableArray contains only one object, there being several passage
NSMutableArray routine and should contain the same number of objects that the counter
show, how code below
if(!isPaused)
{
array = [[NSMutableArray alloc] init];
[filter addAcceleration:acceleration];
[unfiltered addX:acceleration.x y:acceleration.y z:acceleration.z];
NSNumber *number = [[NSNumber alloc] initWithDouble:acceleration.x];
[array addObject:number];
++a;
if (a == 30) // only check the # objs of mutablearray
{
sleep(2);
}
[filtered addX:filter.x y:filter.y z:filter.z];
}
It looks like you're missing a loop of some kind. The code you list above:
Makes sure something isn't paused.
Creates a new (empty) mutable array.
Adds a value to the new array.
And does some other work.
My guess is that this whole if{} block sits inside some kind of loop. You need to alloc and init the mutable array outside of the loop instead.
You create a new array each time the if block is entered, so the addObject: will only add the object to the most recently created array.
Furthermore, you are leaking the array and number objects. Each time you allocate an object, you are responsible for releasing it. Make sure you're familiar with the guidelines set out in the memory management programming guide.

Memory Management for shared array elements

I am a little confused about releasing memory for array elements that are shared by multiple arrays. Here's the scenario:
Manager class has an instance variable NSMutableArray* mgrArray
Helper class has an instance variable NSMutableArray* helperArray.
Manager's init method:
NSMutableArray* mgrArray = [[[NSMutableArray alloc] init] autorelease];
for (int i=0; i<10; i++) {
Food *f = [[[Food alloc] initWithType:#"Fruit"] autorelease];
[mgrArray addObject:f];
}
Helper's init method:
NSMutableArray* helperArray = [[[NSMutableArray alloc] init] autorelease];
The manager object passes some of the mgrArray elements to Helper class to store for Helper's own access purposes (say for efficiency). Some Manager method that does this:
Food *e1 = [mgrArray objectAtIndex:3];
Food *e2 = [mgrArray objectAtIndex:5];
Food *e3 = [mgrArray objectAtIndex:7];
[helper. helperArray addObject:e1];
[helper. helperArray addObject:e2];
[helper. helperArray addObject:e3];
Question 1: when adding e1 to helperArray, should it be copied or retained or is it alright as written above?
Question 2: who should release the memory of the food objects and how?
If you put an object in an array, it retains it. If you remove an object from an array, the array releases it.
It is that simple; ownership -- retains -- should always be bounded by lines of encapsulation.
If you need to ensure that an object remains alive when you are using it, retain it. The one edge case is if you do something like:
foo = [someArray objectAtIndex: 0];
[someArray removeObjectAtIndex: 0];
At that point, foo may have been released. To fix:
foo = [someArray objectAtIndex: 0];
[foo retain];
[someArray removeObjectAtIndex: 0];
.. do stuff ..
[foo release];
Question 1: when adding e1 to
helperArray, should it be copied or
retained or is it alright as written
above?
Simply add e1 to the helperArray. Done.
Question 2: who should release
the memory of the food objects and
how?
When the array is released to the point of being dealloc'd, all objects contained within the array will be released (but not necessarily deallocated unless nothing else holds a retain).
An object will be retained when added to an array and released when removed from an array.
If you are using an autoreleased object there is nothing else to do.
If you are using a regular object, you can release it after it is added to the first array.