Hi, I'm Sam.

I like to make stuff, play music, and write on technology.

Find out more about me.

#objective-c Posts

Archiving Objective-C Objects with NSCoding

Posted on in cocoa, iphone, mac, nscoding, objective-c, and tutorial

For the seasoned Cocoa developer, this is a piece of cake. For newer developers, this can be a real pain, especially if you don't know what you're looking for. I get this question a decent amount, so I figured I'd put a quick guide together.

The Problem

You can't put just any object in a plist. This mainly gets people when they want to put something into NSUserDefaults and get an error (because NSUserDefaults archives to a plist under the hood).

Plists only support the core types: NSString, NSNumber, NSDate, NSData, NSArray, NSDictionary (and their CF buddies thanks to the toll-free bridge). The key here is NSData. You can convert any object to NSData with the NSCoding protocol.

The Solution

There are two things you have to do: implement NSCoding and then use the archiver and unarchiver.

Implementing NSCoding

Say you have an object that looks like this:

@interface Note : NSObject {
    NSString *title;
    NSString *author;
    BOOL published;
}

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *author;
@property (nonatomic) BOOL published;

@end
#import "Note.h"

@implementation Note

@synthesize title;
@synthesize author;
@synthesize published;

- (void)dealloc {
    [title release];
    [author release];
    [super dealloc];
}

@end

Pretty simple, right?

Now, all you have to do to implement NSCoding is the following:

@interface Note : NSObject <NSCoding> {
    NSString *title;
    NSString *author;
    BOOL published;
}

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *author;
@property (nonatomic) BOOL published;

@end
#import "Note.h"

@implementation Note

@synthesize title;
@synthesize author;
@synthesize published;

- (void)dealloc {
    [title release];
    [author release];
    [super dealloc];
}

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.title = [decoder decodeObjectForKey:@"title"];
        self.author = [decoder decodeObjectForKey:@"author"];
        self.published = [decoder decodeBoolForKey:@"published"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:title forKey:@"time"];
    [encoder encodeObject:author forKey:@"author"];
    [encoder encodeBool:published forKey:@"published"];
}

@end

Pretty simple. All I did was add the <NSCoding> protocol delectation in the header file and initWithCoder: and encodeWithCoder: in the implementation. You use these methods to tell NSCoder how to encode your object into data. Notice how two of the variables are objects and one was a BOOL. You have to use different methods for non-objects. The NSCoder documentation has the full list.

Remember, that you can use NSCoder to archive your object however whatever you want. It doesn't have to just be all of the instance variables in your object, although that's what you'll do 90% of the time.

Using the Archiver and Unarchiver

This part is also really easy. Let's say you have an array of notes that you want to put into NSUserDefaults, here's the code:

// Given `notes` contains an array of Note objects
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:notes];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"notes"];

Unarchiving is just as easy:

NSData *notesData = [[NSUserDefaults standardUserDefaults] objectForKey:@"notes"];
NSArray *notes = [NSKeyedUnarchiver unarchiveObjectWithData:notesData];

How to Drastically Improve Your App with an Afternoon and Instruments

Posted on in cocoa, development, instruments, ios, objective-c, and tutorial

I was bragging on Twitter about how I just made my application way better with some simple tweaks. I wanted to write a quick post about what I did that really helped that will probably help most people. This stuff is a bit application specific, but I think you'll see parallels to your application.

Symptoms

My application pulls a ton of data from the network and puts it in Core Data when you login for the first time. From using the application, I noticed that performance totally sucks at first and then goes back to normal. (My table views all scroll at 60fps, but I'll save that for another post. Sorry. Had to throw that in there. I'm way proud.) This was troubling since it usually works really great, (okay, now I'm done bragging about my cells) so I investigated.

Just so you know, I am doing all of my networking, data parsing, and insertion into Core Data on background threads via NSOperationQueue.

The Problems

After running Instruments with the object allocations instrument, I noticed that I was using about 22MB of memory while it was downloading all of this data. In my opinion, that is way too high. I'll add that to list of stuff to mess with.

I also noticed that my NSDate category for parsing ISO8601 date strings (standard way to put a date into JSON) was taking about 7.4 seconds using the timer instrument. Totally unacceptable. Added to the list.

After messing around for a little while longer, I noticed that a lot of time was being spent in one of my NSString categories, specifically in NSRegularExpression. This sounds annoying, so I'll save that for last.

The Solutions

Memory

I had a few guess on how to cut memory usage while converting large amounts of JSON strings into NSManagedObjects. My guess was that a ton of objects needed to be autoreleased but the NSAutoreleasePool wasn't being drained until the operation finished. The simple solution for this to add a well-placed NSAutoreleasePool around problem code. This took a few tries to get in the right spot. I would put it where I think most of the temporary objects were being created and then watch the object allocations instrument to make sure it got flatter.

Here was my first try:

First Try

See how it goes up and drops sharply down a bit and then builds up for awhile then finally drops off? That's a sign there is another loop nested deeper down that should have a pool around it. For the first one, it did a little and then drained (probably because it did less stuff in that operation). Since the second giant hump (note the peak of that is 23MB or so) doesn't drop off for awhile, I know to look for another loop deeper down. Hopefully that makes sense. Once you get in there, it will suddenly hit you after stumbling around for a bit. You'll see.

After moving it to a more nested loop, here's the result:

Second Try

Once I got it in the right spot, it was using under 2MB of memory for the entire process! Score! Next problem.

Date Stuff

The date stuff had me stumped for awhile. I was using ISO8601Parser (a subclass of NSFormatter) which was working really, really well compared to NSDateFormatter. After looking at timer instrument, I saw that most of that time was spent in system classes like NSCFCalendar. I assumed there was a better way. I tried switched back to NSDateFormatter, but that didn't work well and still wasn't great memory and speed wise.

As a disclaimer, I am all about Objective-C. I love it. I'm not one of those engineers that's says "hey, we should rewrite this in C" all the time, but hey, we should rewrite this in C. I did... and the result was astounding!

Here's the code:

#include <time.h>

+ (NSDate *)dateFromISO8601String:(NSString *)string {
    if (!string) {
        return nil;
    }

    struct tm tm;
    time_t t;    

    strptime([string cStringUsingEncoding:NSUTF8StringEncoding], "%Y-%m-%dT%H:%M:%S%z", &tm);
    tm.tm_isdst = -1;
    t = mktime(&tm);

    return [NSDate dateWithTimeIntervalSince1970:t + [[NSTimeZone localTimeZone] secondsFromGMT]];
}


- (NSString *)ISO8601String {
    struct tm *timeinfo;
    char buffer[80];

    time_t rawtime = [self timeIntervalSince1970] - [[NSTimeZone localTimeZone] secondsFromGMT];
    timeinfo = localtime(&rawtime);

    strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S%z", timeinfo);

    return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}

See, it's not too crazy. Using the C date stuff took my date parsing from 7.4 seconds to 300ms. Talk about a performance boost! (I updated SSTookit's NSDate category to use this new code.)

Regular Expression

I have several NSString categories in my application for doing various things. Some of them were called throughout the process I was trying to optimize. I drilled down in the time profiler instrument and realized that [NSRegularExpression regularExpressionWith...] was taking a ton of the time. This totally makes sense, since it compiles your regex to use later and I was doing it each time. Simple solution:

- (NSString *)camelCaseString {
    static NSRegularExpression *regex = nil;
    if (!regex) {
        regex = [[NSRegularExpression alloc] initWithPattern:@"(?:_)(.)" options:0 error:nil];
    }

    // Use regex...

    return string;
}

This was actually the easiest part :)

Conclusions

So using Instruments to track down slow or bad code is really easy once you get the hang of it. Start with the leaks instrument if you're new. You shouldn't have any (known) leaks in your application.

Once you get that down (or get so frustrated trying to track it down you give up and move to something else) do the object allocations instrument next. You can watch the graph and see how many objects you have alive. If you see a big spike that never goes down, you most likely have a ton of memory around that you probably don't need but still have a reference to so it doesn't show up in leaks. Adding autorelease pools around loops that do lots of processing always helps.

Finally, use the time profiler instrument to see what's taking a long time and optimize the crap out of it. This is the most fun since it's easy to see whats happening and how much of an improvement you made by the changes you just made. The key to making this instrument useful is the checkboxes on the left. Turning on Objective-C only or toggling the inverted stack tree is really useful.

This is Hard

Don't feel bad, especially if you're new to this. This stuff is hard. All of my solutions I listed above are pretty simple. I spent almost an entire day coming up with those few things. The majority of the time you spend will be tracking down problems. Fixing them is usually pretty simple, especially after you've done it a few times. This is hard. You're smart. :)

Face Detection at Hipstamatic

Posted on in core-graphics, core-image, face-detection, hipstamatic, and objective-c

Since I joined the ranks at Hipstamatic a few months ago, I've been working on a lot of different things (including some really exciting new stuff I'll be able to show off in a few weeks).

HipstaProcessor

My first big project here was refactoring all of our image processing from Hipstamatic, IncrediBooth, and some upcoming stuff into a library that we can reuse called HipstaProcessor.

This has been a great project. HipstaProcessor runs on iOS and Mac (which I was pretty proud of). This gives us the ability to test out effects quickly as we build them instead of the tedious process of change the effects, putting a build on the device, and testing with whatever images are on the device.

Also, having all of our image processing code in one place makes for lots of reuse of tested and solid code instead of recreating the wheel every time we need to process an image in an app (which is kinda our thing).

Face Detection

Apple added CIDetector in iOS 5, which is really fantastic. It allows you to find faces in images. Here's a really basic example:

// Use high quality detection
NSDictionary *detectorOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                 CIDetectorAccuracyHigh, CIDetectorAccuracy,
                                 nil];

// Create the detector
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace context:ciContext options:detectorOptions];

// Loop through all of the faces it found
for (CIFeature *feature in [detector featuresInImage:ciImage]) {
    // Simply draw a solid color in the face's rect
    // Obviously you could do something much more interesting
    CGContextFillRect(context, feature.bounds);
}

It's actually a lot easier than I thought it would be. It works by finding two eyes and other face features. For people with hair that covers one eye, it won't see them. It also does a pretty poor job at finding people with darker skin, especially in low light.

Since we already had HipstaProcessor going in IncrediBooth, it was easy to simply add face detection to HipstaProcessor and then get it in IncrediBooth instantly. Pretty great stuff.

Haus O' Haunt

Haus O' Haunt Sample In IncrediBooth 1.3 (which comes out October 21st) we added a new booth called Haus O' Haunt that features face detection (among some other new fun effects). This is our Halloween booth, and we went crazy making some spooky effects. It was a really fun time, and I think the effects turned out really great!

The first setting in the booth, called Markup, randomly finds some of the faces and chooses a random marker stroke to place over the face. It's really fun to watch it in action!

So checkout IncrediBooth to see Haus O' Haunt and all of the other new fun effects!

The Future

It's pretty nuts how easy this is now. I remember thinking that face detection is forever away and super hard to do unless you have tons of engineers. It's amazing that Apple has opened this up to everyone. I'm really excited about things to come!

By the way, we're hiring at Hipstamatic. Email me if you want to make some cool iOS or Rails awesomeness: sam@synth.tc

Here's 2 promo codes for IncrediBooth. Hopefully you can snag one and enjoy it for free if you don't already have it! (Only the first two to redeem them get it.)

E3FXNXAPY4PJ
N7WJY6PEXKWJ