Values are overwrite while saving in NSMutableArray - iphone

i am saving the user info in the NSMutableDictionary with key "username" and then saving this Dictionary in another NSMutableDictionary with key "name" and then saving the Dictionary in the NSMutableArray. But the Problem is that whenever i save another user with different username it overwrites the values in the NSMutableArray. Below is the code which i am using
-(void) saveUserData:(NSString *)username
{
NSLog(#"\nsaveDataBarButtonTapped \n");
NSLog(#"the count is %d",[[[ArraysManager sharedInstance] getTotalUserArray] count]);
NSLog(#"ArraysManager description is %# ",[[[ArraysManager sharedInstance] getTotalUserArray] description]);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *userArrayFilePath = [[NSString alloc] initWithString:[directory stringByAppendingPathComponent:#"userData"]];
if (username.length != 0)
{
[dataDictionary setObject:[NSString stringWithFormat:#"%#",username] forKey:#"username"];
[dataDictionary setObject:[NSString stringWithFormat:#"%lf",totalValue] forKey:#"totalValue"];
[dataDictionary setObject:[NSString stringWithFormat:#"%lf",totalPayable] forKey:#"totalPayable"];
[userDictionary setObject:dataDictionary forKey:#"userDictionary"];
[[[ArraysManager sharedInstance] getTotalUserArray] addObject:userDictionary];
[[[ArraysManager sharedInstance] getTotalUserArray] writeToFile:userArrayFilePath atomically:NO];
[dataDictionary removeAllObjects];
[userDictionary removeAllObjects];
NSLog(#"ArraysManager description is %# ",[[[ArraysManager sharedInstance] getTotalUserArray] description]);
}
else
{
UIAlertView *noNameAlertView = [[UIAlertView alloc] initWithTitle:#"No Name Found"
message:#"Please Enter the User Name"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
[noNameAlertView show];
[noNameAlertView release];
}
}

I think you should write:
[userDictionary setObject:dataDictionary forKey:username];
(Actually I don't understand why you do;
[[[ArraysManager sharedInstance] getTotalUserArray] addObject:userDictionary];
it seems like you are adding the same dictionary each time.)

I think you save with the same key on both occasions so it access the same array, you need to change the key for different dictionaries.
EDIT:
[userDictionary setObject:dataDictionary forKey:[NSString stringWithFormat:#"name%d,nameCount"]];

[dataDictionary removeAllObjects];
[userDictionary removeAllObjects];
I think both the above objects are member variables and you are reusing those member variables to add data to your array. Removing objects from dictionary will not create a new dictionary object but are getting updated in same memory location. Your array will have multiple dictionaries and sub-dictionaries but all are having 2 objects of these all at same memory location.
Instead of these member variables, use these as local variables.
Initialize them in this method and add them to array.
Later release them before end of this method.
Hope it helps

You are doing addObject with the same NSMutableDictionary object.
Did you check the count property after addObject?
For quick fix, just copy the dictionary before add it to the array.
[[[ArraysManager sharedInstance] getTotalUserArray] addObject:[[userDictionary copy] autorelease];
(autorelease for non-ARC configuration)

Related

how to add multiple Array in PList

I have a for loop on that for loop I have to store Multiple Array in Plist. But my values are getting overlapped and multiple arrays are not been creating .. what I am doing is
for (i=0;i<10;i++) {
plistDict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
[plistDict setObject:#"a" forKey:#"b"];
[plistDict writeToFile:filePath atomically:YES];
}
This code wont create multiple Array. This will store only once but I need the multiple times array to be created. And every time it should store object and key. What should I do ?
In above code every time your replace old dictionary with new one. This is the reason you have only on dictionary at end of for loop.
try this
NSMutableArray *arr = [NSMutableArray alloc]init];
for (i=0;i<10;i++)
{
plistDict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
[plistDict setObject:#"a" forKey:#"b"];
[arr addObject:plistDict];
}
[arr writeToFile:filePath atomically:YES];}
Try this one.
plistDict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
for (i=0;i<10;i++)
{
NSMutableArray *array=[[NSMutableArray alloc] initWithObjects:#"1",#"2",#"3", nil];
[plistDict setObject:array forKey:[NSString stringWithFormat:#"%d",i]];
}
[plistDict writeToFile:filePath atomically:YES];

NSArray losing values when I put self into it

So I have this piece of code:
- (void) connectSelector:(NSArray *)args {
NSError* error;
NSString* data = [NSString stringWithContentsOfURL:[NSURL URLWithString:[args objectAtIndex:0]] encoding:NSASCIIStringEncoding error:&error];
NSLog(#"%#", data);
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] forKeys:[NSArray arrayWithObjects:self, data, error, nil]];
[[args objectAtIndex:1] performSelector:#selector(dataDownloaderDidDownloadData:) withObject:dictionary];
}
The values NSArray in dictionary is losing its 'self' value. Why is this so?
Thanks in advance!
Looks like you've got the keys and values element back-to-front. You probably meant:
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:self, data, error, nil] forKeys:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] ];
Or to use modern syntax:
NSDictionary *dictionary = #{ #"connector" : self, #"data" : data, #"error" : error };
(Not every object can be used as a key in a dictionary).
Might be the case with arrayWithObjects(but not sure),because it returns an auto-released array.Use initWithObjects which returns an array you must then release to avoid a memory leak.
Check This : iPad large NSArray - initWithObjects vs. ArrayWithObjects

Memory management creating an NSDictionary with NSMutableArrays

I'm having problem understanding memory management when creating a dictionary with mutable arrays. I'm using the ios6 SDK with deployment target 5.1.
In the implementation of the class "Group" the method "namesAndEmails" builds an array "emails" that contains the emails addresses for Person objects with an email. If the Person object does not have an email the Person name is added to another array "namesWithNoEmail". The arrays are returned in a dictionary.
#import "Group.h"
#implementation Group
-(NSDictionary*) namesAndEmails {
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:0] ;
NSMutableArray *namesWithNoEmail = [[NSMutableArray alloc] initWithCapacity:0];
NSString *email;
NSString *name;
for (Person *p in allPersons) {
email = p.email;
name = p.name;
if ([email length]==0) {
[namesWithNoEmail addObject:name];
} else {
[emails addObject:email];
}
}
NSArray *keys = [NSArray arrayWithObjects:#"emails",#"names", nil];
NSArray *objects = [NSArray arrayWithObjects:emails, namesWithNoEmail, nil];
//[emails release];
//[namesWithNoEmail release];
return [NSDictionary dictionaryWithObjects:objects forKeys:keys];
}
Somewhere else in the code I wish to send an email to a group of people so I call the emailGroup method which gets a dictionary out by calling "namesAndEmails" on the group.
-(void) emailGroup:(Group*) g {
NSDictionary *emailInfo = [g namesAndEmails];
guestsWithNoEmail = [emailInfo objectForKey:#"names"];
guestEmails = [emailInfo objectForKey:#"emails"];
int nGuestsWithNoEmail = [guestsWithNoEmail count];
if (nGuestsWithNoEmail > 0) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"No emails" message:#"" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alert show];
}
// some more code here
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:subject];
[picker setMessageBody:#"" isHTML:NO];
[picker setToRecipients:guestEmails];
[[self delegate ] presentModalViewController:picker animated:YES];
[picker release];
}
As far as I understand [NSDictionary dictionaryWithObjects:objects forKeys:keys] in "namesAndEmails" returns an autoreleased dictionary. But why does my code crash if I release the "emails" and "namesWithNoEmail" arrays? I thought that the dictionary would have ownership of the array after they are added and therefore it would be safe to release the arrays in the method. I guess that's not correct, but why?
Is the a more clean way of doing this? Thank you for any advice!
My first suggestion would be to use the "Product->Analyze" feature. If you leaking or over releasing somewhere, it will probably give you the exact chain of events.
Secondly, I can't see the linking between your methods nameAndEmails and emailGroup:. Because I can't see the connection, I can't tell you if the autorelease is causing the problem.
Autoreleased objects get released when the the main run loop cycles. So it's very possible your NSDictionary is getting released. You could test this by doing anything from setting the memory location as a "watch" in the debugger to putting printing something in the console lines each time the runloop your in cycles (I made the assumption your in the main run loop, so correct me if that's not true).
Other things you can do to track the problem would be to use "Zombies" in instruments or NSZombieEnable=YES in your configuration

NSURLRequest converting NSData to an array

I need to convert data received from the web via an array in a PHP script into an array that I can pull values out of. Here's my code!
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//NSString *payloadAsString = [NSString stringWithUTF8String:[receivedData bytes]];
NSArray *payloadAsString = [NSKeyedUnarchiver unarchiveObjectWithData:receivedData];
[payloadAsString finishEncoding];
verified = [payloadAsString objectAtIndex:0];
NSLog(#"logging");
//NSString *no = [[NSString alloc] init stringWithCString:verified];
NSLog(#"%#", verified);
if([verified isEqualToString:#"admin"]){
NSLog(#"test admin");
[self performSelector:#selector(changeViewAdmin) withObject:nil afterDelay:0.05];
}
if([verified isEqualToString:#"user"]){
NSLog(#"test user");
[self performSelector:#selector(changeView) withObject:nil afterDelay:0.05];
}
if([verified isEqualToString:#"No"]){
NSLog(#"test no");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Invalid UserName/Password combination!"
delegate:self
cancelButtonTitle:#"Okay"
otherButtonTitles:nil];
[alert show];
[alert release];
}
[payloadAsString release];
//NSLog(#"%#", verified);
// INSERT GOOGLE MAPS URL REQUEST HERE
/*if(requestType == 1){
NSString* addressText = payloadAsString;
// URL encode the spaces
addressText = [addressText stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding];
NSString* urlText = [NSString stringWithFormat:#"http://maps.google.com/maps?q=%#", addressText];
// lets throw this text on the log so we can view the url in the event we have an issue
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlText]];
// */
//
//}
[connection release];
self.receivedData = nil;
}
Unfortunately, my console returns null and asks if I've put the -finishencoding method in. Question is, if that's correct, where would I do so?
PS: Another question, is if I'm retrieving an array of data from a database, is a PHP script the best way to go? Thank you.
1) Of all this code the only string relevant to your question is
NSArray *payloadAsString = [NSKeyedUnarchiver unarchiveObjectWithData:receivedData];
I really doubt that PHP script returns you data in NSKeyedUnarchiver-compatible format. I believe the only reason you don't get NSInvalidArgumentException exception from this method is that receivedData is nil (did you initialize it anywhere?). Try to make a string from what you receive like this
[[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding] autorelease]
and log it. From this I hope it will be clear how to parse response.
2) Do not name NSArray instances like 'blahBlahString'. Strings and arrays are completely different.
NSKeyedUnarchiver can only unarchive instances which are produced by instances of the NSKeyedArchiver class.
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSKeyedUnarchiver_Class/index.html

Releasing NSArray containing NSDictionary objects

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].)