Creating NSMutableArray with NSDictionary slow elements

This piece of code is a method that creates an array for use by several other classes. The input is an array from a CoreData selection of type NSDictionaryResultType.

The 3 fields are strings that I need to split into arrays, so the components are SeparatedByString.

The resulting array, _dataProductionArray, works fine --- BUT: This piece of code takes FULL 5 SECONDS to process about 32,000 records.

Any help pointing out the egregious bugs that are causing this slow performance would be greatly appreciated!

NSMutableArray *dataArray = [NSMutableArray array];
int j = 0;
int maxNumMonths = 0;
for (id obj in _dictionaries) {
    if ([_dictionaries[j] [@"month"] length] >0 ) {
        // get production values
        NSArray *aItems   = [_dictionaries[j] [@"prodA"] componentsSeparatedByString:@","];
        NSArray *bItems   = [_dictionaries[j] [@"prodB"] componentsSeparatedByString:@","];
        NSArray *monthItems = [_dictionaries[j] [@"month"] componentsSeparatedByString:@","];

        NSMutableArray *productionAArray = [NSMutableArray array];
        NSMutableArray *productionBArray = [NSMutableArray array];
        int monthLoop = 1;
        for (NSNumber *month in monthItems) {
            if (monthLoop <= MONTHS_OF_PRODUCTION) {
                if ([month intValue] == monthLoop) {
                    [productionAArray addObject:[aItems objectAtIndex:monthLoop-1]];
                    [productionBArray addObject:[bItems objectAtIndex:monthLoop-1]];
                    productionCount ++;
                    if (monthLoop > maxNumMonths)
                        maxNumMonths = monthLoop;
                }
            }
            monthLoop++;
        }

        NSDictionary *arrayItem = @{@"name":_dictionaries[j] [@"name"],
                                    @"type":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"type"]],
                                    @"height":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"height"]],
                                    @"aArray":productionAArray,
                                    @"bArray":productionBArray,
                                    };
        [dataArray addObject:arrayItem];
    }
    j++;
}

_dataProductionArray = [NSArray arrayWithArray:dataArray];

      

+3


source to share


2 answers


I can see a few optimizations you could make in the loop, but I'm not sure how much that helps (especially if the compiler does them anyway). The root problem is that 32k is a lot of iterations.

Need all 32k results at once? You could make a significant improvement in the user experience by doing this work lazily , since the user interface requires a transformed entry.

This approach was to make dataProductionArray a volatile dictionary indexed by NSNumber. Then instead of ...

// replace this
self.dataProductionArray[128];

// with this
[self dataProductionAtIndex:@128];

      

This new getter method calls the code you wrote lazily, like ...



- (id)dataProductionAtIndex:(NSNumber *)index {

    // replace dataProductionArray with dataProductionDictionary
    id result = self.dataProductionDictionary[index];
    if (!result) {
        result = [self getDataAt:index];
        self.dataProductionDictionary[index] = result;
    }
    return result;
}

      

Then getDataAt:

is a simple refactor of the code you posted, except instead of looping through 32k elements, it only works for one index that is passed ...

- (id)getDataAt:(NSNumber *)index {

    int j = [index intValue];

    // no loop, just skip to iteration j
    NSArray *aItems   = [_dictionaries[j] [@"prodA"] componentsSeparatedByString:@","];
    NSArray *bItems   = [_dictionaries[j] [@"prodB"] componentsSeparatedByString:@","];

    // and so on, then at the end, don't save arrayItem, just return it

    NSDictionary *arrayItem = @{@"name":_dictionaries[j] [@"name"],
                                @"type":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"type"]],
                                @"height":[NSString stringWithFormat:@"%@",_dictionaries[j] [@"height"]],
                                @"aArray":productionAArray,
                                @"bArray":productionBArray,
                                };
    return arrayItem;
}    

      

PS - Volatile vocabulary is a good data structure for lazy evaluation. The next level of complexity is NSCache, which acts like a mutable dictionary and also manages memory ( ref class here ).

0


source


Your for loop is inactive. Just write

for (NSDictionary* dict in _dictionaries)...

      

and use dict instead of _dictionaries [j]. One method call is saved each time.



stringWithFormat:

creates a new line every time. Can't you just add the element and not turn it into a string?

Instead of fetching all the elements in productionAArray

and out productionBArray

, create NSIndexSet

, fill it in a loop - or better yet, using a block - and create the arrays in one go.

0


source







All Articles