Jayson Lane
Jayson Lane

Reputation: 2818

Adding NSMutableArray to another from a loop seems to create duplicates

I'm parsing through an NSDictionary of json-encoded events and placing them into a two-dimensional NSMutableArray based on their month -- for display in a sectioned table view.

Since I am adding items to an array and then placing that array in an array (event_container) in a loop, event_container shows the correct number of arrays, however, they all appear to be duplicates of the last iteration, so all of the contents of event_container are the same array.

I believe this is because it's a pointer and/or not being released. I'm unsure of an appropriate way around this or possibly even a better solution. I'm using ARC.

int month = 0;
int current_month = 0;
int counter = 0;

event_container = [[NSMutableArray alloc] init];

temp_array = [[NSMutableArray alloc] init];


for (NSDictionary *result in results)
{

    NCEvent *anEvent = [[NCEvent alloc] init];

    anEvent.title = [result objectForKey:@"title"];
    anEvent.startdate = [result objectForKey:@"startdate"];
    anEvent.enddate = [result objectForKey:@"enddate"];

    NSDateFormatter *importDate = [[NSDateFormatter alloc] init];
    [importDate setDateFormat:@"yyyy-M-d H:m:ss"];
    anEvent.dateStart = [importDate dateFromString:anEvent.startdate];
    anEvent.dateEnd = [importDate dateFromString: anEvent.enddate];

    NSDateFormatter *exportDate = [[NSDateFormatter alloc] init];
    [exportDate setDateFormat:@"d"];
    anEvent.text_date = [exportDate stringFromDate: anEvent.dateStart];

    NSDateFormatter *exportMon = [[NSDateFormatter alloc] init];
    [exportMon setDateFormat:@"MMM"];
    anEvent.text_mon = [exportMon stringFromDate: anEvent.dateStart];

    NSDateFormatter *monthInt = [[NSDateFormatter alloc] init];
    [monthInt setDateFormat:@"M"];
    month = [[monthInt stringFromDate: anEvent.dateStart] intValue];

    if(counter == 1){                           //first month
        current_month = month;
        NSLog(@"I'm the first month: %i", month); 
        [temp_array addObject:anEvent];

    }
    else if(month > current_month){             //new month
        NSLog(@"This is a new month");                    
        current_month = month;

        //add the events array to events container and reset the events array
        [self.event_container addObject: temp_array];

        [temp_array removeAllObjects];

        [temp_array addObject:anEvent];

    }
    else{
        NSLog(@"Same Month");                   //same month
        [temp_array addObject:anEvent];


    }

    NSLog(@"Event month integer: %i", month);

    anEvent = nil;

    counter++;

}

Those arrays are declared as properties:

@property (nonatomic, retain) NSMutableArray *event_container;
@property (nonatomic, retain) NSMutableArray *temp_array;

Upvotes: 0

Views: 176

Answers (3)

jscs
jscs

Reputation: 64002

Your suspicions about the array being a pointer is basically correct. The problem is that your temp_array isn't so temporary -- it's in fact the same array object every time through your loop.

You're creating it outside the loop, and whenever you send it addObject: or removeAllObjects, it's affecting the stuff that you've already put in there.

The key part, though, is that when you add the temp_array to event_container, it's the exact same object. It's not copied; the event_container array just gets a pointer to temp_array. When you add it again, it's the same thing. Since event_container just holds a whole bunch of pointers, you end up looking at the same object when you inspect it.

That's what's happening. To solve this, you need to create a separate array for each month; I think that sch's answer will work for you.

A quick demonstration:

NSMutableArray * container = [NSMutableArray array];
NSMutableArray * temp = [NSMutableArray array];

int i;
for( i = 0; i < 5; i++ ){
    [temp addObject:[NSNumber numberWithInt:i]];
    [container addObject:temp];    // Doesn't copy; just adds pointer to temp
    [temp removeAllObjects];
}
// Inspecting container now, we find that it has five arrays, all empty.
NSLog(@"%@", container);

Upvotes: 1

sch
sch

Reputation: 27516

In the line:

[self.event_container addObject: temp_array];

You are always adding the same instance temp_array to self.event_container. This is why you see the same array duplicated many times.

You can solve this by doing the following for example:

-Add the following before your for loop

for (int i = 0; i < 12; i++) {
    [event_container addObject:[NSMutableArray array]];
}
for (NSDictionary *result in results)
...

-Remove

if(counter == 1){                           //first month
     current_month = month;
     NSLog(@"I'm the first month: %i", month); 
     [temp_array addObject:anEvent];
}

-and change the code that comes after that into :

tmp_array = [event_container objectAtIndex:month];
[temp_array addObject:anEvent];

Upvotes: 2

Patrick Pijnappel
Patrick Pijnappel

Reputation: 7655

temp_array is a pointer type (like all objects in objective c). Therefore, with this call:

[self.event_container addObject: temp_array];

...you are adding a pointer to that object to event_container. You are not creating a new array, merely adding multiple pointers to the same object. What you most likely want to do is add a (pointer to a) copy of the object, like this:

[self.event_container addObject: [temp_array mutableCopy]];

Upvotes: 0

Related Questions