BuySellAds.com

Read the chapters in my new book Barcodes with iOS 7 as I hand them in. Great new app opportunities await!
Our DNA is written in Objective-C
Jump

Functions as Parameters – Old & New

This topic is not for the faint of heart because it requires that you permit an additional level of abstraction in your brain to be able to grasp it. Until know all my ivars and properties where either scalar values (like an NSInteger) or pointers to Objective-C instances (like NSString *). But there are cases when you actually want to be able to store more complex functionality yet not have the overhead of object creation and messaging.

From the C days we have a mechanism called “function pointers” and today I’ll show you how you can pass a function itself to an Obj-C class and store it in an instance variable. There are a couple of SDK functions that make use of that.

The we’ll explore the modern-day equivalent of providing this sort of “plug in” dynamic functionality: doing the same thing with blocks. If you’re lucky to have iOS 4.x as minimum requirement for your project then those blocks might be nicer to work with than function pointers.

We assume that we have a class MyClass where another developer should be able to provide a custom function to calculate or perform something on data that you send via parameters.

Old: Function Pointers

Let’s say we want to pass an integer and the custom C-function would return double of the input.

float timesTwo(int x)
{
	return x * 2.0;
}

To make it easier on us, let’s define a type for a pointer to such a function. You can also do without that, but then you’ll have to write many more round brackets and asterisks. A simple typedef saves us lots of work and possible errors.

typedef float(*customFunc)(int);

I know this looks really weird and wrong, but trust me, that’s how you define a type customFunc that points to a function taking an int and returning a float. Note that this is a C-function, not an Objective-C instance method.

Next let me show you a custom class that has a property of this type, one method to make use of that and another where you directly pass in your function.

// .h
typedef float(*customFunc)(int);
 
@interface MyClass : NSObject 
{
	customFunc func;  // ivar for function pointer
}
 
@property (nonatomic, assign) customFunc func;   
 
- (void)useFunctionFrom:(NSInteger)from until:(NSInteger)until;
- (void)doFunction:(customFunc)function;
 
@end
 
// .m
 
@implementation MyClass
 
- (void)useFunctionFrom:(NSInteger)from until:(NSInteger)until
{
	// if func is NULL then calling it crashes
	NSAssert(func, @"Function needs to be set!");
 
	for (NSInteger i=from; i<=until; i++)
	{
		float result = self.func(i);
		NSLog(@"f(%d) = %.2f", i, result);
	}
}
 
- (void)doFunction:(customFunc)function
{
	for (NSInteger i=1; i<=5; i++)
	{
		float result = function(i);
		NSLog(@"f(%d) = %.2f", i, result);
	}
}
 
@synthesize func;
 
@end

You use the customFunc type just like you would use any other variable type. But you need to make sure that it is not NULL because calling a null function pointer raises an exception. That’s what the NSAssert is for.

In the first method we get the function from the synthesized getter (could also use the ivar directly) and call it, passing in i and logging the result. In the second method we call the passed function directly.

It’s more clear if you consider this example:

MyClass *m = [[MyClass alloc] init];
 
m.func = timesTwo;
[m useFunctionFrom:1 until:4];
 
[m doFunction:timesTwo];
 
[m release];

In the first example we pass timesTwo which is literally a pointer to the function in memory that we created above. In the second example we do the same, but directly pass the function pointer instead of parking it in an instance variable.

Now, this might sound very esoteric to you, but as I said there are certain use cases where it made sense to have your code call a custom function as opposed to just setting a multitude of parameters. For example NSArray has a method sortedArrayUsingFunction:context:

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator 
       context:(void *)context

This weird stuff around the (*) we had typedef’ed away, I wonder why Apple didn’t… one function that you might use for this is shown in the documentation:

NSInteger intSort(id num1, id num2, void *context)
{
    int v1 = [num1 intValue];
    int v2 = [num2 intValue];
    if (v1 < v2)        
     return NSOrderedAscending;
        else if (v1 > v2)
    return NSOrderedDescending;
        else
    return NSOrderedSame;
}

You see: two id pointers and a generic context and the return value provides the sort order of the two objects. Without the typedef the function parameter would have looked similar: – (void)doFunction:(float (*)(int))function

New: Blocks

We don’t want to stick forever with the old ways if something better is available and broadly supported. Blocks are such a new technology, available as of iOS 4.0 and most of the user devices are now running this version or higher. So for new projects we can look at the new and shiny stuff.

Blocks are similar in function to function points, but you can put all your code inline and together with the code also all local variables are preserved. At least, that’s how I understand it, honestly I’m just beginning with blocks myself. The Pragmatic Studio did this great introduction to blocks.

We find that when changing MyClass from using a function pointer to using block variables the only relevant change we need to make is replace an * for a ^.

// .h
 
typedef float(^customBlock)(int);
 
@interface MyClass : NSObject 
{
	customBlock blk; // ivar for block
}
 
@property (nonatomic, copy) customBlock blk;  
 
- (void)useBlockFrom:(NSInteger)from until:(NSInteger)until;
- (void)doBlock:(customBlock)block;
 
@end
 
// .m
 
@implementation MyClass
 
- (void)dealloc
{
	[blk release];
	[super dealloc];
}
 
- (void)useBlockFrom:(NSInteger)from until:(NSInteger)until
{
	// if func is NULL then calling it crashes
	NSAssert(blk, @"Block needs to be set!");
 
	for (NSInteger i=from; i<=until; i++)
	{
		float result = self.blk(i);
		NSLog(@"f(%d) = %.2f", i, result);
	}
}
 
- (void)doBlock:(customBlock)block
{
	for (NSInteger i=1; i<=5; i++)
	{
		float result = block(i);
		NSLog(@"f(%d) = %.2f", i, result);
	}
}
 
@synthesize blk;
 
@end

It’s on the calling end that most of the changes are.

EDIT: Turns out that for a block property you need to use copy instead of assign, this will copy the block to the heap where it will continue to live. Blocks are treated like object and so you also need to release copy properties.

MyClass *m = [[MyClass alloc] init];
 
// inline
m.blk = ^(int x) { return (float)(x * 2.0); };
[m useBlockFrom:1 until:4];
 
// block variable
float (^blockTimesTwo)(int) = ^(int x) {
	return (float)(x * 2.0);
};
 
[m doBlock:blockTimesTwo];
 
[m release];

One thing that stumped me at first was that you have to make sure that the value you return from inside the block has to have matching datatypes with the block prototype. Without the specific typecast (float)(…) the result is a double and causes the compiler to complain about that.

The first use has the code to be put into the block property inline. The second use creates a block variable first and then passes this into the second method. This is just to show how it looks without using our special type. It works just the same using that and might be way easier to read:

// block variable
customBlock blockTimesTwo = ^(int x) {
	return (float)(x * 2.0);
};

So far we have exactly the same functionality as with function pointers, but now for the grande finale we’ll find why pros all around prefer blocks nowadays. Can you spot it?

MyClass *m = [[MyClass alloc] init];
 
float mul = 3.0;
 
// block variable
customBlock blockTimesAnything = ^(int x) {
	return (float)(x * mul);
};
 
mul = 10.0;
 
[m doBlock:blockTimesAnything];
 
[m release];

You see that within the {} of the block we still have access to the local variable mul. Furthermore the block sort of takes a snapshot of the variable at the time when it is defined. Setting mul to 10 afterwards has no effect, it’s still using 3. So besides of having the new variables available that you define in the as block parameters, you also get snapshots of local variables.

The typedef trick I have shown in this article greatly simplifies the code. One of the reasons why you generally don’t see this in the wild is that Apple seems to prefer to have the entire type definition in the block parameters so that you don’t have to look up what parameters the block has. It is really up to you whether or not you want this simplification at the cost of a bit of in-transparency. An onlooker would have to go into your header to find what the block parameters and return type should be.

Conclusion

The ability to access snapshots of local variables is what makes blocks orders of magnitude more useful if you want to enable other developers to provide “plug-in” custom functionality to your classes. The other guys can chose whatever ingredients they like at the time of setting the block and are no longer limited to using only the passed parameters or – in case of delegate methods – some ivars.

So if you have a choice then you might want to convert your function pointer properties into block properties, doing so might only require that you change a * to a ^. For this little tweak you open up a new world of customization possibilities for the happy developers who get to use your code.

It’s exactly these benefits why Apple has begun switching to blocks wherever they can, in most cases new methods where added (with blocks as parameters) that can achieve things where previously you had to painstakingly message back and forth.

We say: good idea!


Categories: Recipes

%d bloggers like this: