Duplicate info being written to MArray

Discussion in 'iOS Programming' started by larswik, Nov 20, 2011.

  1. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #1
    I have been scratching my head most afternoon on this so I thought I would now ask.

    The app works but for some reason as I add new client entries the previous ones are being written over by the new one. I am storing some client records in an NSMutableDict (name, job, money and so on.) Then I am storing each dict into an NSMutableArray 1 Dict per index.

    I am using the itemsToSave method to gather all the elements and default values to store in the mutable dict. Then in the writePlist method I am adding the dict to the array and adding a number to the 0 index of that array. For some reason when I add new entries all the previous ones get rewritten to the latest information from the last dict entered?

    Code:
    - (void)writePlist{   
        NSString *aNumber = [NSString stringWithFormat:@"%d", clientContentArray.count];
        [clientContentArray replaceObjectAtIndex:0 withObject: aNumber];
        [clientContentArray addObject:clientInformation]; //add dict to array
        [clientContentArray writeToFile:[self dataFilePath] atomically:YES]; //save to plist
        NSLog(@"The exporting array has %@", clientContentArray); //display content to screen
        
    }
    
    - (void)itemsToSave{
        if (clientInformation) {
            
            [clientInformation setValue:theClientName forKey:@"client"]; //Bellow gathers the info and adds it to the MutableDict
            [clientInformation setValue:theJobName forKey:@"job"];
            [clientInformation setValue:nameCombind forKey:@"tableViewID"];
            [clientInformation setValue:@"0.00" forKey:@"amount"];
            [clientInformation setValue:@"Add Note..." forKey:@"note"];
    
            NSLog(@"Items that are being saved" );
            for(NSString *aKey in clientInformation){
                
                NSLog(aKey);
                NSLog([clientInformation valueForKey:aKey]); //writes out what is in the NSMutableDict.
        }
        }
        else
            NSLog(@"There is not client Information Dict");
    
    }
    
     
  2. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #2
    I think you're reusing the same dictionary object after adding it to the array.
     
  3. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #3
    I will look at the other code in more detail. It seems to be happening sometimes and not other times. I am trying to replicate the problem to identify where the problem is. Since I have no errors in the code it is tough to track down.

    Thanks Jim
     
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Just because your code compiles with no errors, doesn't mean it'll do what you think it should. It's doing exactly what you're telling it to do. It's often our understanding that needs reworking.
     
  5. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #5
    Yes, I get that. The nice things about errors is it directs you to the problem area in your code. When things work with no errors you really need to spend time looking at your code.

    Thanks.
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    When things don't do what you need them to do, I wouldn't exactly call that "working".

    And yes, you need to spend time looking at your code to figure out why it's not doing what you need it to do. In this case, where do initialize clientInformation? What makes you think you are creating a new instance of it for each object you are going to add to your array, rather than re-using the same instance? You probably want to review the difference between a pointer and the memory space it is pointing to.

    The usual approach to populating an array is similar to this:
    Code:
    for (...) {
        ClassName *tempInstance = [[ClassName alloc] init];
        tempInstance.propertyName = propertyValue;
        ...
        [mutableArrayName addObject:tempInstance];
        [tempInstance release]; // if not using ARC
    }
     
  7. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #7
    OK, maybe "working" is a loose term. I thought about that last night and it is funny that you should mention releasing the object. I thought perhaps I should release it after I add it to the NSMutableDict. But sine the Dict is mutable I did not think I needed to. I could just change its values and then add it to the mutableArray. When the app launches it checks the Propertylist file. If it exists it instantiates everything and reads in the plist file. If not it creates new ones

    Code:
    - (void)readPlist{
        NSString *filePath = [self dataFilePath];
        if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
        {
            clientContentArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
            clientListForTable = [[NSMutableArray alloc] init];
            [COLOR="Red"]clientInformation = [[NSMutableDictionary alloc]init];[/COLOR]
            
            NSLog(@"Importing content array has %@", clientContentArray);
            
            for (int i = 1; i < clientContentArray.count;  i++) {
                NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] initWithDictionary:[clientContentArray objectAtIndex:i]];
                [clientListForTable addObject:[tempDict objectForKey:@"tableViewID"]];
                [tempDict release];
            }
            [self.tableView reloadData];
    
        } 
        else{
            [COLOR="Red"]clientInformation = [[NSMutableDictionary alloc] init];[/COLOR]
            clientListForTable = [[NSMutableArray alloc] init];
            clientContentArray = [[NSMutableArray alloc]init];
            [clientContentArray addObject:@"0"];
            [clientContentArray writeToFile: filePath atomically:YES]; // if it can't find it, it writes a blank file.
    
        }
    }
    
     
  8. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #8
    And where are you calling itemsToSave?

    P.S. It would probably help if you provided all the code related to clientInformation rather than us going back and forth asking more questions and providing more answers.
     
  9. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #9
    haha.... The reason I did not provide all the code is because there is a lot and I did not want to over whelm anyone so I thought I would post the problem area, or what I thought would be the problem area.

    So here are the 2 main methods. When the user clicks on a plus button on the navigation bar it calls the applyPaddedHeader method. The user enters the information and clicks the done button which then calls the newBlankTextFields method. This method gathers the information, creates a text object that is stored in an mutableArray to be added to a Tableview. Then saves the information to a Propertylist and pushes a new viewController on screen.

    The new viewController then collects the information from the Plist file to display and be modified as needed.

    Code:
    -(void)newBlankTextFields{
        self.navigationItem.rightBarButtonItem.enabled = YES;
        theClientName = clientNameTextField.text;
        theJobName = jobNameTextField.text;
        NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"MM-dd-yy"];
        
        nameCombind = [NSString stringWithFormat:@"%@ - %@ - %@", [dateFormatter stringFromDate:[NSDate date]], theClientName, theJobName];
    
        [dateFormatter release]; // release the date object
        
        [clientListForTable addObject:nameCombind]; //Adds to array for TableView
    
        [self.tableView reloadData];
        [self itemsToSave]; //Method combinds items to an NSMutableDictionary
        [self writePlist]; // adds the dict to mutableArray and saves to a plist.
        
        ClientViewController *clientViewController = [[ClientViewController alloc]init]; //Bellow pushes new controller on screen
        clientViewController.title=@"Job";
        [self.navigationController pushViewController:clientViewController animated:YES];
        [clientViewController release];
        self.tableView.tableHeaderView = NO; //removes the Header file when exiting to hide it
    }
    
    -(void)applyPaddedHeader{ //Adds a padding and adds textFields to the header
        self.navigationItem.rightBarButtonItem.enabled = NO;
        UIView *header = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 105)] autorelease];
        
        CGRect frame = CGRectMake(14, 10, 300, 25);
        CGRect frame2 = CGRectMake(14, 40, 300, 25);
        
        clientNameTextField = [[UITextField alloc] initWithFrame:frame];
        jobNameTextField = [[UITextField alloc] initWithFrame:frame2];
        
        UIButton *done = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [done addTarget:self action:@selector(newBlankTextFields) forControlEvents:UIControlEventTouchDown];
        [done setTitle:@"Add Client" forState:UIControlStateNormal];
        done.frame = CGRectMake(170, 75, 130, 30);
        [done setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        
        clientNameTextField.borderStyle = UITextBorderStyleRoundedRect;
        clientNameTextField.font = [UIFont boldSystemFontOfSize:15.0];
        clientNameTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        clientNameTextField.placeholder = @"Enter New Client Name";
        clientNameTextField.textAlignment = UITextAlignmentCenter;
        clientNameTextField.delegate = self;
        
        jobNameTextField.borderStyle = UITextBorderStyleRoundedRect;
        jobNameTextField.font = [UIFont boldSystemFontOfSize:15.0];
        jobNameTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        jobNameTextField.placeholder = @"Enter New Job Name";
        jobNameTextField.textAlignment = UITextAlignmentCenter;
        jobNameTextField.delegate = self;
        
        [header addSubview:clientNameTextField];
        [header addSubview:jobNameTextField];
        [header addSubview:done];
        header.backgroundColor = [UIColor clearColor];
    
        self.tableView.tableHeaderView = header;
    
    }
     
  10. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #10
    I reworked the code tonight and that solved the problem. Instead of instantiating the 'clientInformation' in the readlist method and keeping it around to modify and save in to the MutableArray, I alloc/init it right before I use it and then release it right after I add it to the Mutable Array. This solved the buggy problem.

    I guess the lesson here is to create things as you need them and remove them when you don't instead of having them hang around.

    Thanks for your help.
     
  11. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    That's a good lesson to learn, especially when it comes to being a good memory citizen. Of course, you have to weigh this against the efficiency of recreating an object every time you need it. Sometimes it makes sense to take a hit memory-wise in order to improve execution-speed.

    But, I think there is another lesson to be learned here that you should be sure to understand before you move on. And that is: the difference between a pointer and the object it references and how adding an object to an NSMutableArray really just adds a pointer. Are you comfortable with how all that works?
     
  12. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #12
    CLICK... I get it. That explains the problem I was having. I over looked that fact that the mutable array was storing pointers to the objects. So by creating one Mutable Dict that I was in fact just changing that values in the same object and just storing the same pointer into different index number. So when I NSLog what was in the Mutable Array all the pointers were pointing to the same object in memory. That explains why every thing had the same value, it was the same object in memory. I see what my mistake was and that was the lesson learned here.

    Thanks!!!!!
     

Share This Page