简体   繁体   中英

Optimise slow code - enumeration of dictionary

I have the following code that decodes a JSON string into an array of objects that I can then use in a UITableView.

At first I thought the JSON decoding was the slow part but it appears that it is not as the "Dictionary Done" appears almost immediately.

Any ideas on how to get that code to be a little faster?

-(void)parseJSON:(NSString *)jsonData{

    NSLog(@"Start parsing");
    NSDictionary *deserializedData = [jsonData objectFromJSONString];
    NSLog(@"Dictionary Done");

    NSArray *flights = [deserializedData valueForKeyPath:@"flights.flight"];
    NSMutableArray *localArray = [[NSMutableArray alloc] init ];
    NSString *lastFlightno =@"";

    for (NSDictionary *flight in flights){

        ArchiveFlight *aFlight = [[ArchiveFlight alloc] initWithFlightno:[flight objectForKey:@"flightno"] route:[flight objectForKey:@"route"]];
        aFlight.flightID = [flight objectForKey:@"primary_key"];
        aFlight.timeStamp = [aFlight niceDate:[flight objectForKey:@"timestamp"]];

        if (![lastFlightno isEqualToString:aFlight.flightno]) {
            [localArray addObject:aFlight];
        }

        lastFlightno =aFlight.flightno;
        [aFlight release];
    }
    NSLog(@"End Parsing");
    [self loadupTable:localArray];
    self.flightArray = localArray;
    [localArray release];

}

EDIT: Added timestamps

Timestamps of the NSLogs as follows...

2011-04-26 13:22:36.104 App[1778:707] Finished request
2011-04-26 13:22:36.109 App[1778:707] Start parsing
2011-04-26 13:22:36.128 App[1778:707] Dictionary Done
2011-04-26 13:22:37.713 App[1778:707] End Parsing

Sample of the JSON...

{"flights":[{"flight":{"flightno":"RYR54WP","timestamp":"2011-04-26 12:13:04","route":"EGNX-LEAL","primary_key":"836453"}},{"flight":{"flightno":"RYR24LU","timestamp":"2011-04-26 09:14:03","route":"EVRA-EGNX","primary_key":"831318"}},{"flight":{"flightno":"RYR39WH","timestamp":"2011-04-26 05:33:03","route":"EGNX-EVRA","primary_key":"825492"}},{"flight":{"flightno":"RYR7PX","timestamp":"2011-04-25 20:07:03","route":"LELC-EGNX","primary_key":"816703"}},{"flight":{"flightno":"RYR2VB","timestamp":"2011-04-25 16:57:06","route":"EGNX-LELC","primary_key":"810900"}},{"flight":{"flightno":"RYR3JN","timestamp":"2011-04-25 12:36:04","route":"GCTS-EGNX","primary_key":"802631"}},{"flight":{"flightno":"RYR8GV","timestamp":"2011-04-25 06:07:03","route":"EGNX-GCTS","primary_key":"792945"}},{"flight":{"flightno":"RYR82QR","timestamp":"2011-04-24 19:42:04","route":"EPKK-EGNX","primary_key":"783306"}},{"flight":{"flightno":"RYR51PV","timestamp":"2011-04-24 16:31:05","route":"EGNX-EPKK","primary_key":"777835"}},{"flight":{"flightno":"RYR53AQ","timestamp":"2011-04-24 14:09:05","route":"LIME-EGNX","primary_key":"773572"}},{"flight":{"flightno":"RYR1CX","timestamp":"2011-04-24 11:02:05","route":"EGNX-LIME","primary_key":"768285"}},{"flight":{"flightno":"RYR9ZW","timestamp":"2011-04-24 08:21:04","route":"LEGE-EGNX","primary_key":"764624"}},{"flight":{"flightno":"RYR63BC","timestamp":"2011-04-24 05:48:02","route":"EGNX-LEGE","primary_key":"761726"}},{"flight":{"flightno":"RYR7PX","timestamp":"2011-04-23 19:39:03"

Formatted sample:

{
   "flights":[
      {
         "flight":{
            "flightno":"RYR54WP",
            "timestamp":"2011-04-26 12:13:04",
            "route":"EGNX-LEAL",
            "primary_key":"836453"
         }
      },
      {
         "flight":{
            "flightno":"RYR24LU",
            "timestamp":"2011-04-26 09:14:03",
            "route":"EVRA-EGNX",
            "primary_key":"831318"
         }
      }
   ]
}

EDIT 2:

So here is "niceDate" that is causing the slowdown!

-(NSString *)niceDate:(NSString *)oldDate{
    NSDateFormatter *formatter = [[[NSDateFormatter alloc] init]autorelease];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *sourceDate = [formatter dateFromString:oldDate];
    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
    [dateFormatter setDateStyle:NSDateFormatterFullStyle];
    [dateFormatter setTimeStyle:NSDateFormatterLongStyle];
    NSString *timeString = [dateFormatter stringFromDate:sourceDate];
    return [NSString stringWithFormat:@"%@",timeString];
}

Some things that come to mind:

NSArray *flights = [deserializedData valueForKeyPath:@"flights.flight"];

Do you need to use KVC? What is the structure of your JSON data?

ArchiveFlight *aFlight = [[ArchiveFlight alloc] initWithFlightno:[flight objectForKey:@"flightno"] route:[flight objectForKey:@"route"]];
aFlight.flightID = [flight objectForKey:@"primary_key"];
aFlight.timeStamp = [aFlight niceDate:[flight objectForKey:@"timestamp"]];

You always create an instance of ArchiveFlight and parse the timestamp…

    if (![lastFlightno isEqualToString:aFlight.flightno]) {
        [localArray addObject:aFlight];
    }

…even though you don't have to do that all the time. Depending on how many repeated flightnos you have, this can make a noticeable difference.

Why not read [flight objectForKey:@"flightno"] , compare it to lastFlightno and, if and only if they're different, create an instance, add it to the array, and release it?


Edit: Try the following KVC-free code:

NSArray *flights = [deserializedData objectForKey:@"flights"];
NSMutableArray *localArray = [[NSMutableArray alloc] init ];
NSString *lastFlightno =@"";

for (NSDictionary *flightWrapper in flights) {
    NSDictionary *flight = [flightWrapper objectForKey:@"flight"];
    NSString *flightno = [flight objectForKey:@"flightno"];

    if (! [flightno isEqual:lastFlightno]) {
        // create instance, add it to the array, release the instance
    }
}

Edit: You're creating and (auto)releasing two instances of NSDateFormatter inside this method. In general this would be okay, but since it is being executed >1K times there are two considerations: a) you're creating/using/releasing those two instances >1K times when, in fact, you don't have two, b) you should use an autorelease pool in your loop.

You should make this method a class method (or a function) since it doesn't depend on the state of an instance of that class. Your formatters would be class (static) variables. For instance:

@implementation ArchiveFlight

static NSDateFormatter *formatter1; // choose better names!
static NSDateFormatter *formatter2;

+ (void)initialize {
    if (self == [ArchiveFlight class]) {
        formatter1 = [[NSDateFormatter alloc] init];
        [formatter1 setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

        formatter2 = [[NSDateFormatter alloc] init];
        [formatter2 setDateStyle:NSDateFormatterFullStyle];
        [formatter2 setTimeStyle:NSDateFormatterLongStyle];
    }
}

+ (NSString *)niceDate:(NSString *)oldDate {
    NSDate *sourceDate = [formatter1 dateFromString:oldDate];
    NSString *timeString = [formatter2 stringFromDate:sourceDate];
    return timeString;
    // why +stringWithFormat:? It’s not necessary!
    // return [NSString stringWithFormat:@"%@",timeString];
}

This fixes item a) but you really should use an autorelease pool inside your loop because the Cocoa methods you're using return autoreleased objects. By using an autorelease pool for each iteration of your loop, you reduce the memory footprint of your code — although this could decrease performance as well. I suggest you try both with and without an inner autorelease pool.

for (NSDictionary *flightWrapper in flights) {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    …

    [pool drain];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM