Archives For iPhone

I was curious about the difference, if any, in performance between SBJSON and YAJL with Objective-C bindings. SBJSON is a strict JSON parser written in Objective-C. YAJL is a small event-driven (SAX-style) JSON parser written in ANSI C. Awhile back I wrote YAJL Objective-C bindings and I did some very basic speed tests on JSON data from twitter, delicious, last.fm and yelp. I ran these tests on the original, 3G and 3GS iPhones.

The SBJSON test ran NSString#JSONValue, and the equivalent YAJL test ran NSData#yajl_JSON. The YAJL bindings operate directly on NSData, and this makes a bit more sense since we usually have NSData first, whereas for SBJSON you have to decode to a string before parsing. I didn’t include this difference in my tests though.

These are tests of parsing only (and not loading of any data or strings) of the 4 APIs datasets run 100 times on the original iPhone, 3G and 3GS:


iphone


iphone3g


iphone3gs


So the YAJL framework gives about a 60% speed increase. The YAJL Objective-C bindings can be found on github.

Be sure to thank @lloydhilaiel for making YAJL full of so much awesomeness.

Update: The project used to do the perf test can be found here.

I recently was investigating using a more lightweight JSON parser for iPhone projects (other than the standard SBJSON library) and came across the YAJL C JSON library. While using it as a document style parser didn’t result in a huge performance gain, it does a support a streaming “SAX” style parser which might help memory usage in larger documents.

Hopefully, sometime soon I will do some performance comparisons. In the meantime, if you try these bindings, let me know how it goes. You can find it on github at: http://github.com/gabriel/yajl-objc.

Invoking forward

May 22, 2009 — 2 Comments

Typically when you want to invoke methods on a separate thread, with a delay, back on the main thread after a long operation, etc,  you are stuck with the performSelector:onThread: methods. With performSelector you are limited to invoking with a limited number of arguments, which must be objects; it won’t auto-unbox NSValue/NSNumber/NSNull like key value coding does.

This limitation can be really frustrating, particularly when you are using NSOperation or threading in general and need your delegates to call back onto the main (UI) thread.

A possible solution is to use NSInvocation with performSelector to deal with multiple arguments and primitives, for example:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(listWithOffset:limit:)]];
[theInvocation setSelector:@selector(listWithOffset:limit:)];
[theInvocation setTarget:self];
NSInteger offset = 40;
[theInvocation setArgument:&offset atIndex:2];
NSInteger limit = 20;
[theInvocation setArgument:&limit atIndex:3];
[invocation performSelector:@selector(invoke) onThread:thread withObject:nil waitUntilDone:NO];

but it gets cumbersome, especially setting up the invocation instance. (BTW, the first argument is at index 2 because of the hidden arguments self and _cmd.) Also you would need to call retainArguments if your arguments were objects, that may be released by the calling thread.

Thankfully there is a better way. In a post called ‘Grab that Invocation‘ and later at Dave Dribbin’s post ‘Invoke on Main Thread‘ we can see how to get an NSInvocation instance from an NSProxy/forwardInvocation: automatically; and now we can combine the proxy with performSelector:onThread: or any of the other peformSelector methods to invoke back on the main thread, invoke on other threads, delay invocation, or other more general aspects like timing methods, debugging, logging, or security.

You can find Dave Dribbins original DDInvocationGrabber implementation in his DDFoundation library.

I’ve expanded on it a bit in GHKit, (see GHNSInvocationProxy) and plan on adding more features. If you use it with the GHNSObject+Invocation category, it gets even better:

// Invoke after 2 second delay (useful for simulating slow operations)
[[self gh_proxyAfterDelay:2.0] listWithOffset:40 limit:20];

// Invoke on main thread (if you ran on a separate thread or NSOperation
// and wanted to call back the delegate on the main thread)
[self listWithOffset:40 limit:20 delegate:[delegate_ gh_proxyOnMainThread:YES]];
// Later, after listing, the connection invokes the delegate back on the 
// main thread
[delegate_ connection:self didListWithOffset:40 limit:20];

// Invoke on thread
[[self gh_proxyOnThread:thread waitUntilDone:NO] listWithOffset:40 limit:20];

// Time the invocation
NSTimeInterval time;
[[self gh_timedProxy:&time] listWithOffset:40 limit:20];
NSLog(@"Took %0.2fs", time);

This category also includes other performSelector helpers (supporting var args and argument lists).

What might be some other ways to use this? Maybe an NSOperationProxy that allows you to queue and prioritize invocations, or more complex debugging or analytics proxies that can keep stats of certain activities.

I’ve always wondered why XCode doesn’t have a unit testing GUI like other IDEs, or why the SenTesting framework has to be such a pain to setup, with all its RunScript build phases, shell scripts, octest bundle insanity. After using the GTMTestCase for the iPhone (since SenTesting isn’t supported on the iPhone SDK) I decided to try to re-purpose some of the GTM Unit Testing code into a standalone testing framework and GUI that I could use on both my Mac OS X and iPhone projects.

GHUnit (pronounced ˈgü-ˈnit, I guess?), hosted at gabriel/gh-unit, is meant to be installed as a framework, and run as an application in a separate Test target. The idea being that you can run and crash into the XCode debugger directly and utilize all the debugging techniques that you normally use. The test GUI should allow you to see test failures more clearly, view timings / stats and not have to go fishing for the build console window. And an automated way to view stack traces.

Details on how to use the framework are included in the README.

GHUnit Test GUI for Mac OS X App

GHUnit Test GUI for Mac OS X App

Unit Test GUI for iPhone App

Unit Test GUI for iPhone App

For the iPhone side, I included a similar GUI that runs the tests in the simulator.

GHUnit can be used as a standalone test framework (by subclassing GHTestCase), or with your existing SenTestCase tests or GTMTestCase tests.

So far its pretty basic, but I am using it on a couple projects and its been helping my development a bunch and letting me to write tests as I go without completely destroying my workflow or sanity.

Any feedback is appreciated and let me know if you have any problems with the install/embed instructions or in general. Also thanks to the GTM peeps, on which much of this is based.

Maybe the next step is an XCode plugin?

Its always been a little painful when you get an exception and the debugger decides not to cooperate, leaving you with something like:

Stack Trace

Thanks!

As far as I could work out, traditional methods of grabbing a symbolic stack trace don’t work on the iPhone. (If I am remembering right, I don’t think NSStackTraceKey exists in UIKit.)

A little while ago I hacked the GTMStackTrace from google-toolbox-for-mac, to lookup the symbolic stack trace from the call stack return addresses (NSException#callStackReturnAddresses introduced in 10.5) and print it out using NSSetUncaughtExceptionHandler. Life has been a little bit easier ever since. Thankfully, the awesome people who work on GTM added it in themselves (see GTMStackTrace.h#61). Here is how you might set it up, in main.m:

#ifdef DEBUG
#import 
#import "GTMStackTrace.h"

void exceptionHandler(NSException *exception) {
	NSLog(@"%@", GTMStackTraceFromException(exception));
}
#endif

int main(int argc, char *argv[]) {
    
#ifdef DEBUG
	NSLog(@"Debug enabled");	
	NSDebugEnabled = YES;
	NSZombieEnabled = YES;
	NSDeallocateZombies = NO;
	NSHangOnUncaughtException = YES;
	[NSAutoreleasePool enableFreedObjectCheck:YES];
	NSSetUncaughtExceptionHandler(&exceptionHandler);
#endif	

	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int retVal = UIApplicationMain(argc, argv, nil, nil);
	[pool release];
	return retVal;
}

For me, DEBUG is set in GCC_PREPROCESSOR_DEFINITIONS via the awesome GTM xcconfg files, which you should also be using. Hopefully, you should get a trace that looks like:

[Session started at 2008-12-30 00:10:13 -0800.]
2008-12-30 00:10:15.772 TestExceptionIPhone[8493:20b] Debug enabled
2008-12-30 00:10:15.830 TestExceptionIPhone[8493:20b] #0  0x92c5614b _NSExceptionSetRubyToken()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#1  0x9657ce3b objc_exception_throw()  (/usr/lib/libobjc.A.dylib)
#2  0x92c55f2b +[NSException raise:format:arguments:]  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#3  0x92c55f6a +[NSException raise:format:]  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#4  0x002350 +[TestExceptionIPhoneAppDelegate applicationDidFinishLaunching:]  (/Users/gabe/Library/Application Support/iPhone Simulator/User/Applications/586E48B6-B758-4DBD-A534-D6927DA74583/TestExceptionIPhone.app/TestExceptionIPhone)
#5  0x30a4e01a +[UIApplication performInitializationWithURL:asPanel:]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#6  0x30a57363 +[UIApplication _runWithURL:]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#7  0x900425ee +[NSRunLoop runMode:beforeDate:]  (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation)
#8  0x92bdcb45 CFRunLoopRunSpecific()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#9  0x92bdccf8 CFRunLoopRunInMode()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#10 0x31564600 GSEventRunModal()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices)
#11 0x315646c5 GSEventRun()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices)
#12 0x30a4ec98 +[UIApplication _run]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#13 0x30a5a094 UIApplicationMain()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
2008-12-30 00:10:15.834 TestExceptionIPhone[8493:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Give me a good stack trace'

And the source of the exception usually starts around the 5th line.