If you have a data intensive app where you do a lot of JSON parsing, or, god forbid, XML or SOAP you may have noticed that you are not using all the processing power at hand.
Let’s look at a simple example of loading a long list of users:
NSURL* resourceURL = [[NSBundle mainBundle] URLForResource:@"users" withExtension:@"json"]; NSArray* usersRaw = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:resourceURL options:NSDataReadingMappedAlways|NSDataReadingUncached error:nil] options:0 error:nil]; NSMutableArray* users = [NSMutableArray new]; [usersRaw enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) { APUser* user = [APUser instanceFromDictionary:object]; [users addObject:user]; }];
If you have a look at this code running in Instruments you will see the following:
One of the cores on a dualcore CPU is always idle. You’re not doing anything for 50% of the time. Let’s fix that! Since NSMutableArray isn’t thread safe, we’ll also add a lock. You need to customize this to work with your own code. It’s not worth to trade a slow app for a faster one that’s crashing so take care and make sure your code is thread safe!
NSURL* resourceURL = [[NSBundle mainBundle] URLForResource:@"users" withExtension:@"json"]; NSArray* usersRaw = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:resourceURL options:NSDataReadingMappedAlways|NSDataReadingUncached error:nil] options:0 error:nil]; NSMutableArray* users = [NSMutableArray new]; NSLock *arrayLock = [[NSLock alloc] init]; [usersRaw enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id object, NSUInteger idx, BOOL *stop) { APUser* user = [APUser instanceFromDictionary:object]; [arrayLock lock]; [users addObject:user]; [arrayLock unlock]; }];
Let’s have a look at the Instruments graph again. The CPU is now being utilized to it’s fullest potential!
The key is this part:
enumerateObjectsWithOptions:NSEnumerationConcurrent
What does this mean in actual numbers? The enumeration block runs in ~1 second instead of ~1.7 seconds on an iPhone 5s with a 37MB JSON file. While you may not be parsing that much data at once in your app, over the course of a long app run you can easily reach those figures, so I believe this will still make a difference. NSEnumerationConcurrent is available in iOS 4+ and Mac OS 10.6+ so it should be safe for pretty much anyone to use.