Adding multiple NSObjects to an NSMutableArray - objective-c

With the following code, both items in the array are the same (the last item). What am I doing wrong that is causing this array to overwrite the values? I'm trying to use 1 object so I don't have to instantiate X number of objects.
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj = [[MyObjClass alloc] init];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:obj];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj];
In MyObjClass.h I have #interface MyObjClass : NSObject. Is NSObject the wrong datatype?
Properties in MyObjClass.h:
#property (strong) NSString *firstName;
#property (strong) NSString *lastName;
And from MyObjClass.m:
#synthesize firstName, lastName;

The array isn't overwriting the values, you are in your code. You have one instance of the MyObjClass. *obj is a pointer to that objects and when you add it to the array twice, the array has two indexes that point to the object you added twice.
By setting the properties on obj, you're changing the values of that one object that both your *obj pointer and the array points to.
Why are you concerned about instantiating X objects? It sound like you want X objects which are in the array with distinct values.
Besides creating X objects, you can copy the first object, set the values then add that to the array but since you're setting all the values anyways, I'm not sure why you just wouldn't init a new object.
EDIT:
Based on your comment below, it looks like your concern of multiple objects is around memory management. When you add an object to an array, it retains the object so after you add it (if you're done with it in that scope), then release or autorelease it. When the array is released, it will call release on all the objects in the array. You need n objects whether you init or copy - you still have multiple. Release them and then let the array release when it's released.

You need to create a unique instance for each object that is added. Also the obj needs to be released in this method because adding it to the array retains it. However if you are using ARC no releasing is needed/allowed.
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj;
obj = [[[MyObjClass alloc] init] autorelease];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:obj];
obj = [[[MyObjClass alloc] init] autorelease];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj];

You are misunderstanding the -addObject: method and objects in general. You are adding a reference (pointer) to your instance of MyObjClass to the array. If you add the same instance twice, it doesn't make a difference. Also, as you only store a pointer to the object in the array, you always have the latest "version" of your object in the array.
You need to allocate two instances and add them both:
MyObjClass *object1 = ...;
[array addObject:object1];
MyObjClass *object2 = ...;
[array addObject:object2];

When adding objects to NSMutableArray, they are retained, not copied. If you don't want to modify your included object, use this sequence of code:
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj = [[MyObjClass alloc] init];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:[[obj copy] autorelease]];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj]; // if you're using it further then you need to make a copy of it again

Try set the properties in your MyObjClass class like this:
#property (copy) NSString *firstName;
#property (copy) NSString *lastName;
#property (copy) NSString *created;

Related

Default list in Objective C?

I want to store data at run time, I can have a linked list and add at runtime, however as I am new to IOS and objective C, do we have any default list in which we can add our datas, (Datas are two stings and an integer).
Cocoa provides NSArray and NSMutableArray, a pair of ordered containers similar to Java's ArrayList and C#'s List. You can add values to NSMutableArray, and it will grow as you add more elements; NSArray is read-only.
You can use .plist files to store your data.
Read more 'Loading data from .plist files', 'How to use plist in iphone?' or google it like 'load data from .plist'. However you can create NSArray or something like this at run time. If you want dive deeper you must read ObjC Collections Programming Topics
Make a class for your data with default properties and make sure it inherits NSObject then use NSMUtableArray to add/remove elements to the list.
// in the .h file of your object
#interface MyObject : NSObject {
NSString* strAttribute1;
// add more attributes as you want
}
#property (nonatomic, retain) NSString* strAttribute1;
#end
// then in the .m file
// do not forget the #import ""
#implement MyObject
#synthesize strAttribute1;
// override the dealloc to release the retained objects
#end
then in your code where you want to make a list of this object
NSMutableArray* myArray = [[NSMutableArray alloc] init];
// add elements and iterate through them
// do not forgot to free the memory if you are not using ARC
[myArray release];
You can use NSArray or NSMutableArray or NSDictionary or NSMutableDictionary depending on your needs.
NSArray:
NSArray *myArray;
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = #"a string";
myArray = [NSArray arrayWithObjects:aDate, aValue, aString, nil];
NSMutableArray:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = #"a string";
[myArray addObject:aDate];
[myArray addObject:aValue];
[myArray addObject:aString];
NSDictionary:
NSDictionary * myDict = [NSDictionary dictionaryWithObjects:aDate, aValue, aString forKeys:firstDate, firstValue, firstString];
NSMutableDictionary:
NSString *aString = #"a string";
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
myDict = [[NSMutableDictionary alloc] init];
[myDict setObject:aString forKey:firstString];
[myDict setObject:aDate forKey:firstDate];
[myDict setObject:aValue forKey:firstValue];

I have a memory leak in this objective-c method, can anyone tell me where?

I'm receiving an exc_bad_access somewhere in the code below. I don't understand where it is if anyone could shine any light on it? It's a method that takes in an NSMutableArray of dictionaries and sorts them by one of the elements in the dictionary. The memory leak is almost certainly in the bit with the block but I think i'm missing something fundamental in finding it...
-(NSMutableArray*)sortBicyclesByDistanceToDevice:(NSMutableArray*)inputArray{
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b){
NSNumber *first = [[a objectForKey:kDistanceFromDevice] objectForKey:kValue];
NSNumber *second = [[b objectForKey:kDistanceFromDevice] objectForKey:kValue];
return [first compare:second];}];
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
[arrayToHoldSorted release];
return [retVal autorelease];
}
Thanks
It looks like you assign retVal to an NSMutableArray through then reassign immediately after. The original alloced NSMutableArray will leak. That is:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
Should be:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
Replace:
NSMutableArray *retVal = [[NSMutableArray alloc] init];
retVal = [arrayToHoldSorted mutableCopy];
With:
NSMutableArray *retVal = [arrayToHoldSorted mutableCopy];
You are leaking the first value of retVal.
There's more than one in there!
This line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
Is a memory leak since you immediately reassign the pointer. It should be removed. Just declare your array on the next line:
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator...
This method returns an autoreleased object, so you don't need to release it later on.
A similar pattern with the mutable array. You alloc/init, then overwrite with a new object, giving another leak. Again, remove the alloc/init line and just declare in the next line. mutableCopy gives you an implicitly retained object, so you do need to autorelease it.
You seem to be under the impression that alloc/init is needed every time you declare an object variable. This is not the case.
You allocate arrayToHoldSorted (1) - which you never use as you then get an NSArray back from sortedArrayUsingComparator(2). And then you release it afterwards(3) when you don't own it. You do the same trick for retVal, allocating a NSMutableArray - then overwriting your reference to it by getting a new NSMutableArray from [arrayToHoldSorted mutableCopy];
NSArray *arrayToHoldSorted = [[NSArray alloc] init]; .. // 1
arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) ..... // 2
[arrayToHoldSorted release]; // 3
Just assign the return NSArray from sortedArrayUsingComparator to a reference...
NSArray* arrayToHoldSorted = [inputArray sortedArrayUsingComparator:^(id a, id b) .....
I think the problem is that in this line:
return [retVal autorelease];
you release something that you have not retained. Also in this line:
NSArray *arrayToHoldSorted = [[[NSArray alloc] init];
you have an extra [, which does not help. But most importantly, you can use the static analyzer in XCode to diagnose this sort of bug, rather than pestering the good folk on StackOverflow.

Memory management, should I be retaining?

If I declare an NSString in my header file as follows:
{
NSString *testString;
}
#property(nonatomic,retain) NSString *testString;
Then synthesize the string in my .m file, what is the correct way to initialise it?
If I don't write:
self.testString = [[NSString alloc] init];
then the it is never initialised, but if I do, then isn't the string being retained twice? Or should I initialise it some other way, such as:
self.testString = [NSString stringWithFormat:#"%#, sampleText];
You are correct, the former will over retain the object.
Use the second form instead.
If you had to use the ivar directly however, you need to retain the object :
testString = [[NSString stringWithFormat:#"%#, sampleText] retain];
self.testString = [NSString stringWithFormat:#"%#, sampleText]; or self.testString = [NSString string]; is correct; self.testString = [[NSString alloc] init]; will cause over-retaining.
Consider using ARC (Automatic Retain Counting) for you project. With ARC the compiler takes care of retain counts so you don't have to, in fact aren't allowed to. There is a refactoring that will convert a current project.
It seems that you declare an variable called testString in your .h and you also create a property that retains.
You can either use this:
self.testString = [NSString string];
or you can use
testString = [[NSString alloc] init];
Defining the variable through the property will cause it to be retained, that's why you declared it as (nonatomic, retain). Defining the variable through the declaration won't take those arguments into consideration. Here's a quick rule of thumb about retaining.
Using your code as a base:
self.testString = [[NSString alloc] init]; // Retain count = 2
self.testString = [NSString string]; // Retain count = 1
testString = [[NSString alloc] init]; // Retain count = 1
testString = [NSString string]; // Not retained at all.
First of all #property (nonatomic, copy) NSString *testString to avoid mutability bugs.
Second - if you want just a string without leaks:
self.testString = [NSString string];
self.testString = [[[NSString alloc] init] autorelease];
self.testString = [NSString stringWithFormat:#"%#", text];
these are all valid options.

Memory management while copying objects

I know that my question has already been discussed on StackOverflow but i found the answer not complete for my needs. So the question is:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
secondArray = [firstArray mutableCopy];
what is retain count for the secondArray now? 2 or 1? Should i release it twice or just once?
Does copy or mutableCopy increases retain count of the COPYING (secondArray in this event) object?
You should never care about the absolute retain count. Only that you're "balanced", that means for every alloc, new*, copy, mutableCopy and retain you need a corresponding release or autorelease (when not using ARC, that is).
If you apply this rule to each line you can see that your second line has an alloc, but there's no release. In fact, it's absolutely useless to allocate an instance here since you're not interested in it anyway. So it should simply read:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [firstArray mutableCopy];
// There is no third line.
But let's discuss your original code and see what happened:
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
// secondArray points to a new instance of type NSMutableArray
secondArray = [firstArray mutableCopy];
// You have copied another array (created a new NSMutableArray
// instance) and have overwritten the pointer to the old array.
// This means that the instance allocated in line 2 is still there
// (was not released) but you don't have a pointer to it any more.
// The array from line 2 has been leaked.
In Objective-C, we often speak of ownership: there are very few methods that make you the "owner" of an object. These are:
alloc
new*, as in newFoo
copy and mutableCopy
retain
If you call these, you get an object for which you are responsible. And that means you need to call a corresponding number of release and/or autorelease on these objects. For example, you're fine if you do [[obj retain] retain]; and then [[obj autorelease] release];
NSMutableArray *firstArray = [[NSMutableArray alloc] initWithObjects: obj1,obj2,nil];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
secondArray = [firstArray mutableCopy];
What is happening is that you've created a memory leak. You just lost the reference assigned to secondArray when you overwrote it with the mutableCopy of firstArray with this line.
secondArray = [firstArray mutableCopy];
If you then release secondArray twice, the program will crash because you're then overreleasing the mutable array assigned by
secondArray = [firstArray mutableCopy];
What you need to do is to make sure you're not overwriting retained references by mistake, and balance retains with releases.

Fill NSMutableArray from another NsMutableArray

I have two classes indexViewController and flashCardQuestionViewController.
In the indexViewController i have table filled with an array.
Now i am getting some data from the database:
-(void)getMultipleChoiceAnswer
{
if(optionid!=nil)
[optionid removeAllObjects];
else
optionid = [[NSMutableArray alloc] init];
if(optionText!=nil)
[optionText removeAllObjects];
else
optionText = [[NSMutableArray alloc] init];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:"select optionID,OptionText from flashCardMultipleAnswer where questionId=1"];
while(sqlite3_step(dataRows) == SQLITE_ROW)
{
[optionid addObject:[NSNumber numberWithInt:sqlite3_column_int(dataRows,0)]];
[optionText addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)]];
}
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
and I am calling this method in the viewDidLoad method of the indexViewController.
Now I have another NSMutableArray in the flashCardQuestionViewController named listNoOfOptionsInQuestion.
I want to fill listNoOfOptionsInQuestion with objects from optionText array in indexViewController.
How can I do this?
There are a number of ways to copy arrays: you can either use -[NSArray copy] to get an immutable copy, or -[NSArray mutableCopy] for a mutable copy. Don't forget that copy adds a reference so you'll need a release or autorelease somewhere (if you're not using GC that is).
Alternatively, you can use -[NSMutableArray addObjectsFromArray:].
Given your example, it looks like you want to do something like this at the end:
[flashCardQuestionViewController setListNoOfOptionsInQuestion:optionText];
And then in FlashCardQuestionViewController, you want something like:
- (void)setListNoOfOptionsInQuestion:(NSArray *)options
{
if (options != listNoOfOptionsInQuestion) {
[listNoOfOptionsInQuestion release];
listNoOfOptionsInQuestion = [options mutableCopy];
}
}
Rahul,
Do you really need to have a completely different copy of the MutableArray in each object. Would it be possible to have both objects point to the same array? For instance:
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = mArray;
Or do you need to make changes to the two arrays in different ways? The try this (as suggested by Chris above) :
ClassOne *one = [[ClassOne alloc] init];
ClassTwo *two = [[ClassTwo alloc] init];
// build mutable array mArray
// ...
one.objectArray = mArray;
two.objectArray = [mArray mutableCopy];
again, if this isn't what you need then you'll have to give us a more precise question or problem that we can identify.