Archive for 2013

Debouncing Method Dispatch in Objective-C

I’ve been doing a lot of Node.js programming recently, and Javascript / Coffeescript programming style is starting to boil over into the way I think about Objective-C. One of my favorite practices in Javascript is the concept of deferring, throttling, or debouncing method calls. There are tons of uses for this. For example, let’s say your app is updating a model object, and you want to persist that model object when the updates are complete. Unfortunately, the model is touched in several bits of code one after another, and your model gets saved to disk twice. Or three times. Or four times. All in one pass through the run loop.

It’s pretty easy to defer or delay the execution of an objective-c method using “performSelectorAfterDelay” or any number of methods. Debouncing—running a method just once in the next pass through the run loop after it’s been called multiple times—is a bit trickier. However, in the case described above it’s perfect. Touch the model all you want, call “save” a dozen times, and in the next pass through the run loop, it get’s saved just once.

Here’s how I implemented a new debounce method “performSelectorOnMainThreadOnce”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@implementation NSObject (AssociationsAndDispatch)
 
- (void)associateValue:(id)value withKey:(void *)key
{
	objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN);
}
 
- (void)weaklyAssociateValue:(id)value withKey:(void *)key
{
	objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_ASSIGN);
}
 
- (id)associatedValueForKey:(void *)key
{
	return objc_getAssociatedObject(self, key);
}
 
- (void)performSelectorOnMainThreadOnce:(SEL)selector
{
    [self associateValue:[NSNumber numberWithBool: YES] withKey: (void*)selector];
 
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self associatedValueForKey: (void*)selector]) {
            [self performSelector: selector];
            [self associateValue:nil withKey:(void*)selector];
        }
    });
}
 
@end

A more advanced version of this method would allow you to say “perform this selector just once in the next 100 msec” rather than performing it in the next iteration through the run loop. Anybody want to take a stab at that?

Teaching kids to code: It’s not about fame and fortune.

I’ve been programming since I was seven. I started with Cocoa, the children’s language developed by Apple in 1996 (they recycled the Cocoa brand when they bought NeXT). Then I learned AppleScript, REALBasic, and eventually Objective-C and Java. I have code so old that it’s full of spelling mistakes: I learned to program before I could spell trivial words like “building.” My personal journey as a programmer was propelled by my dad who has been developing Mac shareware for more than 25 years, but it mirrors what might happen if programming is taught in schools. For the most part, that’s really exciting.

This week’s code.org campaign put the power and celebrity of today’s most successful developers behind an effort to teach the world to code. But the reasons they champion for learning to program—namely, fame, fortune, and a great job with free food—seem oddly genderless. When I excitedly played through their latest ad, I was left thinking to myself: “They pulled together our industry’s best and brightest, and that’s all they came up with?”

Over the last few years, I’ve been in more than 30 classrooms—in elite private schools with Macbooks for every student, and in inner city schools on the edge of collapse. My research in educational technology has brought me in touch with the kids code.org is trying to reach, and I think all of them would benefit from learning to code. But when I look at the impact programming has had on my life—especially on my childhood, marked by school transitions, my parent’s divorce, and a fair bit of high school drama, it has done far more for me than provide a steady job.

For kids, programming is about zen. It’s about learning patience. It’s about feeling accomplished when the world around you seems to be rooting for your failure. It’s about being able to sit down at a $250 Chromebook and envelop yourself in a world where known rules apply, where constant input produces constant output. A world you can learn to understand and use to create whatever your heart desires. For kids for whom most of the world makes no goddamn sense, it’s an amazing gift.

I understand why code.org is marketing to students the way it is—you don’t tell inner city kids to play basketball because it’ll keep them in shape and doesn’t require grass, you tell them they can be like Michael Jordan. What upsets me is that the individuals who tell their personal stories in their latest video—people who have a deep, personal affiliation with the art, present it in such an objective way. If I were featured in that video, my cameo would by dramatically different. Programming taught me patience and process, and it’s given me balance when I’ve needed it most.

Discuss this post on Hacker News.

targetContentOffsetForProposedContentOffset: not called

Just a quick tip. If you’re subclassing UICollectionViewLayout or UICollectionViewFlowLayout and targetContentOffsetForProposedContentOffset:withScrollingVelocity: is not being called, check to make sure your UICollectionView does not setPagingEnabled: YES. If you have paging enabled on the collection view, that setting takes precedence over the targetContentOffset.

stringWithFormat: is slow. Really slow.

I’m working on a project that makes extensive use of NSDictionaries. Buried deep in the model layer, there are dozens of calls to stringWithFormat: used to create dictionary keys. Here’s a quick example:

1
2
3
4
5
6
7
8
- (CGRect)rect:(NSString*)name inDict:(NSDictionary*)dict
{
    float x = [[dict objectForKey:[NSString stringWithFormat: @"%@@0", name]] floatValue];
    float y = [[dict objectForKey:[NSString stringWithFormat: @"%@@1", name]] floatValue];
    float w = [[dict objectForKey:[NSString stringWithFormat: @"%@@2", name]] floatValue];
    float h = [[dict objectForKey:[NSString stringWithFormat: @"%@@3", name]] floatValue];
    return CGRectMake(x, y, w,h);
}

In this example, I’m using stringWithFormat: in a simple way. To read four CGRect values for the rect ‘frame’ from the dictionary, it creates the keys [email protected], [email protected], [email protected], and [email protected] Because of the way my app works, I call stringWithFormat: to create strings like this a LOT. In complex situations, to the tune of 20,000x a second.

I was using Instruments to identify bottlenecks in my code and quickly discovered that stringWithFormat: was responsible for more than 40% of the time spent in the run loop. In an attempt to optimize, I switched to sprintf instead of stringWithFormat. The result was incredible. The code below is nearly 10x faster, and made key creation a negligible task:

1
2
3
4
5
6
7
8
- (NSString*)keyForValueAtIndex:(int)index inPropertySet:(NSString*)name
{
        // the following code just creates %@@%d  - but it's faster than stringWithFormat: by a factor of 10x.
        char cString[25];
        sprintf (cString, "@%d", ii);
        NSString* s = [[[NSString alloc] initWithUTF8String:cString] autorelease];
        return [name stringByAppendingString: s];
}

It’s worth mentioning that I’ve refactored the app even more—to completely avoid saving structs this way (since NSValue is uh… an obvious solution), but I felt like it was worth posting this anyway, since you might not be able to refactor in the way I did.