I am trying to store a variety of strings and a NSMutableArray in a NSMutableDictionary. Basically an XML element with various key/values and one subelement with a list.
NSMutableArray *subItem = [[[NSMutableArray alloc] init] autorelease];
for (GDataXMLElement *subElement in [key children]){
[subItem addObject:[[[self runThroughElement:subElement] copy] autorelease]];
}
[item setObject:[[subItem copy] autorelease] forKey:[key name]];
When I check the type:
NSLog(#"Categories type: %#", [[item objectForKey: #"categories"] classForCoder]);
I get:
Categories type: NSArray
But then in the function which deals with the data and stores it in an object it suddenly appears to be a string:
NSLog(#"Categories type: %#", [[obj objectForKey: #"categories"] classForCoder]);
Which results in:
Categories type: NSString
I am at a loss what is going wrong, why do I get a string out of the dictionary and not the array I put into it? Any help would be really appreciated!
(probably need more info, so please let me know what is relevant)
You shouldn't really be using classForCoder. Instead just log [[obj objectForKey:#"categories"] class]. I'd also try logging the straight [obj objectForKey:#"categories"] to see if it does output a string or an array. If it outputs a string then either obj is not item or your dictionary is being modified at some point.
One bit of advice, there isn't any reason to do [[[NSMutableArray alloc] init] autorelease]. Just call [NSMutableArray array]. Does the same thing but with less code. Same goes for many classes, look for convenience class methods to save you having to do alloc/init/autorelease.
Related
I´m doing an app and I can´t get a mutable array to accept objects. I´v tried setting breakpoints to see what´s happening but it keeps saying that the mutable array is nil. Does anyone has an answer?
My code:
- (void)save:(id) sender {
// All the values about the product
NSString *product = self.productTextField.text;
NSString *partNumber = self.partNumberTextField.text;
NSString *price = self.priceTextField.text;
NSString *quantity = self.quantityTextField.text;
NSString *weigh = self.weighTextField.text;
NSString *file = [self filePath];
//Singleton class object
Object *newObject = [[Object alloc] init];
newObject.product = product;
newObject.partNumber = partNumber;
newObject.price = price;
newObject.quantity = quantity;
newObject.weigh = weigh;
//Array declaration
mutableArray = [[NSMutableArray alloc]initWithContentsOfFile: file];
[mutableArray addObject:newObject];
[mutableArray writeToFile:file atomically:YES];
}
While initWithContentsOfFile: can be called on an NSMutableArray, it was inherited from NSArray. The return value is an NSArray which is not mutable. If you want to add objects to your mutable array, you have to do something like this:
mutableArray = [[[NSMutableArray alloc] initWithContentsOfFile: file] mutableCopy];
[mutableArray addObject:newObject];
[mutableArray writeToFile:file atomically:YES];
Now, the addObject: call should work.
Best regards.
[NSMutableArray initWithContentsOfFile:] returns nil by default if the file can't be opened or parsed. Are you sure the file you're loading exists and is formatted correctly?
Try to check with break point on
mutableArray = [[NSMutableArray alloc]initWithContentsOfFile: file];
Line. Move your cursor on mutableArray if it shows you __NSArrayI that means it is an immutable array i.e. you cant update it and if it shows you __NSArrayM that means it is a mutable array and you can update this array.
In your case you're getting immutable array thats why you cant update it.
So you have two way to get mutable Array from this file -
Method:1
mutableArray = [[[NSMutableArray alloc] initWithContentsOfFile: file] mutableCopy];
Method:2
NSArray *anyArray = [[NSArray alloc]initWithContentsOfFile: file];
mutableArray = [[NSMutableArray alloc]initWithArray:anyArray];
In both case mutableArray woud be a mutable Array. You can update it.
I have a NSMutableArray (i.e. "stories") of dictionary items.
I can read the dictionary items in the array just fine, e.g.
[[[stories objectAtIndex: b] objectForKey: #"title"]
But, now I am trying to update (i.e. replace) a couple of objects e.g. "title" & "matchingword", but I cannot find the right code. Any suggestions are much appreciated.
I tried this, but it seems to be adding entirely new objects to the array
NSMutableDictionary *itemAtIndex = [[NSMutableDictionary alloc]init];
[itemAtIndex setObject:[[placesArray objectAtIndex:a]objectAtIndex:0] forKey:#"reference"];
[stories replaceObjectAtIndex:x withObject:itemAtIndex]; // replace "reference" with user's unique key
[itemAtIndex release];
I also tried this (but didn't work either):
//NSMutableDictionary *itemAtIndex2 = [[NSMutableDictionary alloc]init];
//[itemAtIndex2 setObject:[separatePlaces objectAtIndex:x] forKey:#"matchingword"];
//[stories insertObject:itemAtIndex2 atIndex:x]; // add the unique matching word to story
//[itemAtIndex2 release];
Help appreciated. Thanks.
You need to grab the dictionary you want to mod.
NSMutableDictionary *temp = [stories objectAtIndex: b];
The change the value:
[temp setObject:#"new Info" forKey:#"title"];
Suppose I am holding data in an array like this
wordList = [[NSMutableArray alloc] init];
while ([rs next]) //Some database return loop
{
wordDict = [[NSMutableDictionary alloc] init];
[wordDict setObject:[NSNumber numberWithInt:[rs intForColumn:#"id"]] forKey:#"id"];
[wordDict setObject:[rs stringForColumn:#"word"] forKey:#"word"];
[wordList addObject: wordDict];
[wordDict release];
wordDict = nil;
}
But I want to store this result (i.e. wordList) in SQLite for later use - I guess using NSCoding. How would I do that?
(Feel free to point out any errors in how stuff is being alloc'ed if there are problems there).
If you don’t insist on serialization using NSCoding, there’s a writeToFile:atomically: method both on NSArray and NSDictionary. This will serialize your object into a property list (*.plist). The only catch is that all the objects in the “tree” to be serialized must be NSString, NSData, NSArray, or NSDictionary (see the documentation). I’m not sure how NSNumber fits in, but with a bit of luck it will be serialized and deserialized too. The inverse method that will turn the file back into a dictionary or an array is called initWithContentsOfFile:.
As for your code, I would just use the [NSMutableDictionary dictionary] convenience method that gets you an autoreleased dictionary. It’s shorter than the usual alloc & init and you save one line for the explicit release.
I am having difficulty getting my head around memory management in the following segment of code on iPhone SDK 3.1.
// Create array to hold each PersonClass object created below
NSMutableArray *arrayToReturn = [[[NSMutableArray alloc] init] autorelease];
NSArray *arrayOfDictionaries = [self generateDictionaryOfPeople];
[arrayOfDictionaries retain];
for (NSDictionary *dictionary in arrayOfDictionaries) {
PersonClass *aPerson = [[PersonClass alloc] init];
for (NSString *key in [dictionary keyEnumerator]) {
if ([key isEqualToString:[[NSString alloc] initWithString: #"FIRST_NAME"]])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:[[NSString alloc] initWithString: #"LAST_NAME"]])
aPerson.lastName = [dictionary objectForKey:key];
}
// Add the PersonClass object to the arrayToReturn array
[arrayToReturn addObject: aPerson];
// Release the PersonClass object
[aPerson release];
}
return arrayToReturn;
The [self generateDictionaryOfPeople] method returns an array of NSDictionary objects. Each NSDictionary object has two keys "FIRST_NAME" and "LAST_NAME" with a person's first name and last name as the respective data. The code is looping through each dictionary object in the arrayOfDictionaries array and assigning the dictionary data to the relevant property of an aPerson (PersonClass) object. This object is then added to an array which is returned from this method.
When running instruments I am getting a leak for the dictionary objects contained in the arrayOfDictionaries array. The code within the [self generateDictionaryOfPeople] method is calling [dictionaryObject release] on each NSDictionary object as it is created and added to the array, which makes the retain count on the object 1 (as adding the object to the array would make the retain count 2, but then my release message decrements it back to 1).
I assume this leak is because I am never releasing the arrayOfDictionaries array, and thus the NSDictionary objects within the array are never released. If I attempt to release the array at the end of the above segment of code I get a "message sent to deallocated instance" error. I understand why this is occurring, because I am assigning the aPerson object data within a dictionary item (that I am subsequently releasing) but I don't know where else I can release the arrayOfDictionaries array. Any help would be greatly appreciated.
Thanks!
EDIT: Below is the implementation for [self generateDictionaryOfPeople]
- (NSArray *)generateDictionaryOfPeople {
NSMutableArray *arrayFromDatabase = [[[NSMutableArray alloc] init] autorelease];
// ** Query the database for data **
while ( there are rows being returned from the database ) {
// Declare an NSMutableDictionary object
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
// Loop through each column for that row
for ( while there are columns for this row ) {
columnTitle = title_of_column_from_database
columnData = data_in_that_column_from_database
// Add to the dictionary object
[dictionary setObject:columnData forKey:columnTitle];
// Release objects
[columnName release];
[columnTitle release];
}
// Add the NSMutableDictionary object to the array
[arrayFromDatabase addObject:dictionary];
// Release objects
[dictionary release];
}
// Return the array
return arrayFromDatabase;
}
Here,
if ([key isEqualToString:[[NSString alloc] initWithString: #"FIRST_NAME"]])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:[[NSString alloc] initWithString: #"LAST_NAME"]])
aPerson.lastName = [dictionary objectForKey:key];
Replace them with
if ([key isEqualToString:#"FIRST_NAME"])
aPerson.firstName = [dictionary objectForKey:key];
else if ([key isEqualToString:#"LAST_NAME"])
aPerson.lastName = [dictionary objectForKey:key];
The problem of the leak is you're creating 1 ~ 2 NSString-s per loop without -release-ing them. If you need constant NSString-s, just directly use them.
I am still getting the original leak due to not releasing the arrayOfDictionaries array.
That means you forgot to autorelease it in generateDictionaryOfPeople.
You need to review the memory management rules.
You are not releasing arrayFromDatabase. (The simplest way to avoid this kind of mistake is to use factories and autorelease as early as possible rather than defer releases manually. In this case, use [NSMutableDictionary dictionary] instead of [[NSMutableDictionary alloc] init].)
Is it acceptable to have a NSMutableArray within an NSDictionary? Or does the NSDictionary also have to be mutable?
The NSMutableArray will have values added to it at runtime, the NSDictionary will always have the same 2 NSMutableArrays.
Thanks,
Dan
Yes, it's perfectly acceptable. Keep in mind, the contents of the array are the pointers to your NSMutableArrays--those are what can't change in the immutable dictionary structure. What the pointers point to can change all you want. To wit:
NSMutableArray *arr = [[NSMutableArray alloc] init];
NSDictionary *dict = [NSDictionary dictionaryWithObject:arr forKey:#"test"];
[arr addObject:#"Hello"];
NSString *str = [[dict objectForKey:#"test"] objectAtIndex:0];
NSLog("%#", str);
It's quite acceptable. But, it's precisely the sort of setup that suggests you should seriously consider replacing the dictionary with an NSObject subclass that sports two properties for accessing the arrays.