Archive for August, 2014

Objective-C Blocks as Tuples Replacement

Thursday, August 14th, 2014

When programming you sometimes want to return multiple values from a function. The problem is that many languages only support one return value per function. A commonly used solution is to pass values by reference as input parameters to the function and “misuse” these as output parameters. Some languages (or libraries) – e. g. Swift – provide a tuple type where you can pack multiple values together so you can return multiple values as a tuple. You can also put your return values in some other kind of container, e. g. a dictionary or an array. All these solutions have their pitfalls, mostly some kind of loosing the type of the return values or having to box and unbox primitive values or just being cumbersome or hard to read.

Recently I came up with another idea how to get multiple return values from a function: If the used language supports some sort of closures (e. g. blocks in Obj-C) simply let the caller pass a closure that receives the return values with their proper types and call that closure to return the values.

An example in Objective-C:
Let’s create a class extension to NSArray that assumes the array just contains NSString objects and searches for the string that appears most often in that array. The result values are the number of occurrences of the string and the string itself.

@interface NSArray (MyExtension)
- (void)findMostUsedString:(void (^)(int count, NSString *string))result;
@end

@implementation NSArray (MyExtension)

- (void)findMostUsedString:(void (^)(int count, NSString *string))result
{
	// do the heavy calculation:
	int maxCount = 0;
	NSString *maxString = nil;
	@autoreleasepool {
		NSMutableDictionary *counters = [NSMutableDictionary dictionary];
		for(NSString *string in self) {
			@autoreleasepool {
				int count = [counters[string] intValue] + 1;
				counters[string] = @(count);
				if (count > maxCount) {
					maxCount = count;
					maxString = string;
				}
			}
		}
	}

	// return the result consisting of multiple values
	// by calling the passed block:
	result(maxCount, maxString);
}

@end

Now you can use that function returning two values as simple as that:

NSArray *array = @[@"foo", @"bar", @"foo", @"foobar", @"42", @"foo"];
[array findMostUsedString:^(int count, NSString *string) {
	NSLog(@"Found \"%@\" %d times", string, count);
}];

Output is:

Found "foo" 3 times