Fill NSMutableArray from another NsMutableArray - objective-c

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.

Related

Seg Fault when using addObject to NSMutableArray

I seem to be having a problem with the NSMutableArray.
Here's my code:
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
while(condition) {
NSInteger temp = someNumber;
[returnArray addObject: temp];
}
But as soon as it hits the addObject message, the program seg faults. Any advice?
You can't add primitives like integers to an array, only objects (hence the name addObject:). If you want to add numbers, you have to convert them to an NSNumber, or one of the related classes.
You can only add objects to array and NSInteger is not an array.
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
while(condition) {
[returnArray addObject: [NSNumber numberWithInt: someNumber]];
}
You need to wrap primitives such as NSInteger into an NSNumber class. You can do the following:
while(condition)
{
NSInteger temp = someNumber;
[returnArray addObject:#(temp)];
}
Or if your compiler doesn't support that syntax:
while(condition)
{
NSInteger temp = someNumber;
[returnArray addObject:[NSNumber numberWithInteger:temp]];
}

Fast way to search the properties of objects in an NSArray

I have an NSArray of custom objects that all have a #property name of type NSString. How can I quickly enumerate through the array and create a new array that contains only the objects that have a specific word in their name property?
For instance:
CustomObject *firstObject = [[CustomObject alloc] init];
firstObject.name = #"dog";
CustomObject *secondObject = [[CustomObject alloc] init];
secondObject.name = #"cat";
CustomObject *thirdObject = [[CustomObject alloc] init];
thirdObject.name = #"dogs are fun";
NSMutableArray *testArray = [NSMutableArray arrayWithObjects:firstObject,
secondObject,
thirdObject,
nil];
// I want to create a new array that contains all objects that have the word
// "dog" in their name property.
I know I could use a for loop like so:
NSMutableArray *newArray = [NSMutableArray array];
for (CustomObject *obj in testArray)
{
if ([obj.name rangeOfString:#"dog"].location == NSNotFound) {
//string wasn't found
}
else {
[newArray addObject:obj];
}
}
But is there a more efficient way? Thanks!
NSString *searchString = #"dog";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains %#", searchString];
NSArray *filteredArray = [testArray filteredArrayUsingPredicate:predicate];
Please have a look at NSPredicates ! They are highly efficient when you are searching / filtering through array results. This is the documentation!

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.

Method Creates an Array with 11 objects, All Out of Scope, Unrecognized Selector Results

Okay, so, I'm doing a simple lookup. I have an array of NSString objects and a string to search for in the array's elements.
It all seems to work up until I try to add a match to a new mutable array made to hold the search results. The stringHolder variable gets the string, and resultsCollectorArray even get the right number of new elements, but each element is empty and "out of range". Here's the method:
#implementation NSArray (checkForString)
-(NSMutableArray *) checkForString: (NSString *) matchSought
{
long unsigned numberofArrayElements;
long unsigned loop = 0;
NSRange searchResults;
NSMutableArray * resultCollectorArray = [[NSMutableSet alloc] init];
id stringHolder;
numberofArrayElements = [self count];
while (loop < numberofArrayElements) {
searchResults.length = 0;
searchResults = [[self objectAtIndex: loop] rangeOfString: matchSought options:NSCaseInsensitiveSearch];
if (searchResults.length > 0) {
stringHolder = [self objectAtIndex: loop];
[resultCollectorArray addObject: stringHolder];
}
loop++;
}
return [resultCollectorArray autorelease];
}
Once we get back to the main portion of the program, I get an unrecognized selector sent to the mutable array that was supposed to receive the result of the method. Here's the main section:
#import <Foundation/Foundation.h>
#import "LookupInArray.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *testString = [[NSString alloc] initWithString: #"ab"];
NSMutableString * resultString = [[NSString alloc] init];
NSArray * theArray = [[NSArray alloc] initWithObjects: ..., nil]; // Actual code has the objects
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSUInteger arrayCount = 0;
unsigned long loops = 0;
resultArray = [theArray checkForString: testString];
arrayCount = [resultArray count];
while (loops < arrayCount){
resultString = [resultArray objectAtIndex: loops]; // Here's where we get the unrecognized selector.
NSLog(#"%#", resultString);
loops++;
}
[pool drain]; // Also, I'll release the objects later. I just want to get what's above working first.
return 0;
}
I've searched the other answers (for hours now), but didn't seen anything that solved the issue.
Any and all help would be really appreciated.
And thanks beforehand.
NSMutableArray * resultCollectorArray = [[NSMutableSet alloc] init]; is so incorrect. You are creating a mutable set and assigning it to a mutable array.
You are getting unrecognized selector because objectAtIndex: is not a valid selector for NSMutableSet. Make that statement,
NSMutableArray * resultCollectorArray = [[NSMutableArray alloc] init];
A Better way
NSArray * filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchString]];
You can directly filter the array using predicates. This way you do this in a single step. :)

Different Keys Point to Same Object(s) in NSMutableDictionary

I have a custom object called Person that among other things contains an NSString field called descriptor, which stores what sort of person that Person object is (angry, sad, wild, happy, morose, etc). All of my Person objects are in an NSMutableArray, but I would like to store them in an NSMutableDictionary in such a manner:
Key: A, Object: An NSMutableArray where all Person objects have descriptor starting with 'A'
Key: B, Object: An NSMutableArray where all Person objects have descriptor starting with 'B'
Key: C, Object: An NSMutableArray where all Person objects have descriptor starting with 'C'
etc...
I've tried to do this in my code below, and at the comment //POINT 1, the keys and arrays seem to match up, but at //POINT 2, when I print out the complete dictionary, all the keys come up with the same values!
So I wanted to know why the NSMutableArray I seem to have is not being stored as I want it in the NSMutableDictionary?
- (void)buildDictionaryForIndexList {
NSMutableDictionary *tempDict = [[[NSMutableDictionary alloc] init] autorelease];
NSMutableArray *personsStartingWithLetter = [[NSMutableArray alloc] init];
NSMutableArray *indexList = [[[NSMutableArray alloc] init] autorelease];
NSInteger loopCounter = 1;
NSString *firstLetter = [[[NSString alloc] init] autorelease];
for (Person *v in persons) {
firstLetter = [[v descriptor] substringWithRange:NSMakeRange(0, 1)];
if ([indexList containsObject:firstLetter]) {
[personsStartingWithLetter addObject:v];
if (loopCounter == [persons count]) {
[tempDict setObject:personsStartingWithLetter forKey:firstLetter];
}
} else {
if (loopCounter > 1) {
//POINT 1
NSLog(#"%#",[indexList objectAtIndex:[indexList count]-1]);
for (Person *q in personsStartingWithLetter) {
NSLog(#"%#",[q descriptor]);
}
[tempDict setObject:personsStartingWithLetter forKey:[indexList objectAtIndex:([indexList count] - 1)]];
[personsStartingWithLetter removeAllObjects];
}
[indexList addObject:firstLetter];
[personsStartingWithLetter addObject:v];
} // else
loopCounter++;
} // for
//POINT 2
NSEnumerator *enumerator = [tempDict keyEnumerator];
for (NSString *str in enumerator) {
NSLog(#"%#",str);
for (Person *c in [tempDict objectForKey:str]) {
NSLog(#"%#",[c descriptor]);
}
}
self.dictionary = tempDict;
} // buildDictionaryForIndexList
So, for example, at POINT 1 my output is:
A
Angry
Amiable
B
Belligerent
C
Cool
...
W
Wild
but at POINT 2 my output is
T
Wild
J
Wild
A
Wild
...
W
Wild
Change [tempDict setObject:personsStartingWithLetter forKey:[indexList objectAtIndex:([indexList count] - 1)]]; (just after point 1) to [tempDict setObject:[[personsStartingWithLetter copy] autorelease] forKey:[indexList objectAtIndex:([indexList count] - 1)]];. The problem is that NSDictionary copies the key, but retains the value. Therefore, if you add a mutable array to the dictionary and then change it, the array in the dictionary also changes. You need to create a non-mutable copy of the array to put in the dictionary.
The whole method is a bit overcomplicated.
- (void)buildDictionaryForIndexList
{
NSMutableDictionary *tempDict = [[[NSMutableDictionary alloc] init] autorelease];
for (Person *v in persons)
{
NSString* firstLetter = [[v descriptor] substringWithRange:NSMakeRange(0, 1)];
NSMutableArray* personsStartingWithLetter = tempDict [firstLetter];
if (personsStartingWithLetter == nil)
{
personsStartingWithLetter = [NSMutableArray array];
tempDict [firstLetter] = personsStartingWithLetter;
}
[personsStartingWithLetter addObject:v];
} // for
self.dictionary = tempDict;
}
You start with an empty dictionary that will contain arrays. For every person, you check whether there is a suitable array or not, and if there isn't one, you create it. So now there is an array for the person, so you add it to the array. That's all.