Tuesday, February 03, 2009

Function Templates in Objective-C

If you're lucky enough to experience my TwitStream lately, then you might have noticed that I've been doing some iPhone development. That means I'm working in Objective-C. It's reflection capabilities are interesting, to say the least. Low overhead reflection does allow for some code templates. Take a look at this.

double MeanTime(SEL selector){
NSArray *dataList = GetDataList();
if ([dataList count] == 0){
return 0.0;
double cnt = (double) [dataList count];
double sum = 0.0;
for (StatData *data in dataList){
if ([data respondsToSelector:selector]){
sum += [[data performSelector: selector] doubleValue];
return sum/cnt;
double MeanDownloadTime(void){
return MeanTime(@selector(downloadTime));
double MeanProcessingTime(void){
return MeanTime(@selector(processingTime));
double MeanTotalTime(void){
return MeanTime(@selector(totalTime));

Yeah, I know, what's up with the C-style functions? This was inherited code where I was only changing the implementation of those functions, and I didn't feel like wrapping them in a nice pretty class just for the blog. The StatData class has several synthesized properties, including downloadTime, processingTime, and totalTime. I needed three functions that iterated over a list of StatData and computed the average of these properties. Objective-C's selectors provided a nice way to do this. I have to admit that I tried to over-think this at first, and tried using @selector(getDownloadTime:) at first, instead of @selector(downloadTime). I thought that this would be the method name that the compiler would synthesize from the property. This didn't even work, but the more obvious, straightforward usage did.

1 comment:

Vincent said...

If an item in the dataList does not respond to selector, then MeanTime() will return an incorrect average. It will divide by the number of items in dataList not the number of items from dataList that were summed.

(If having a dataList full of different items isn't something legal, then I would just take off the the respondsToSelector: test. That way, if something crazy gets passed in, it'll fail faster).