How to write Objective-C Blocks inline?

7

10

I am trying to implement a binary search using objective-c blocks. I am using the function indexOfObject:inSortedRange:options:usingComparator:. Here is an example.

// A pile of data.
NSUInteger amount = 900000;
// A number to search for.
NSNumber* number = [NSNumber numberWithInt:724242];

// Create some array.
NSMutableArray* array = [NSMutableArray arrayWithCapacity:amount];
for (NSUInteger i = 0; i < amount; ++i) {;
    [array addObject:[NSNumber numberWithUnsignedInteger:i]];
}
NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];

// Run binary search.
int index1 = [array indexOfObject:number 
                    inSortedRange:NSMakeRange(0, [array count]) 
                          options:NSBinarySearchingFirstEqual 
                  usingComparator:^(id lhs, id rhs) {
                      if ([lhs intValue] < [rhs intValue]) {
                          return (NSComparisonResult)NSOrderedAscending;
                      } else if([lhs intValue] > [rhs intValue]) {
                          return (NSComparisonResult)NSOrderedDescending;
                      }
                      return (NSComparisonResult)NSOrderedSame;
                  }]; 
NSTimeInterval stop1 = [NSDate timeIntervalSinceReferenceDate]; 
NSLog(@"Binary: Found index position: %d in %f seconds.", index1, stop1 - start);

// Run normal search.
int index2 = [array indexOfObject:number];
NSTimeInterval stop2 = [NSDate timeIntervalSinceReferenceDate];
NSLog(@"Normal: Found index position: %d in %f seconds.", index2, stop2 - start);   

I wonder how I can use an externally defined objective-c block with the aforementioned function. Here are two compare functions.

NSComparisonResult compareNSNumber(id lhs, id rhs) {
    return [lhs intValue] < [rhs intValue] ? NSOrderedAscending : [lhs intValue] > [rhs intValue] ? NSOrderedDescending : NSOrderedSame;
}
NSComparisonResult compareInt(int lhs, int rhs) {
    return lhs < rhs ? NSOrderedAscending : lhs > rhs ? NSOrderedDescending : NSOrderedSame;
}

Those are written in reference to the following declarations which can be found in NSObjCRuntime.h.

enum _NSComparisonResult {NSOrderedAscending = -1, NSOrderedSame, NSOrderedDescending};
typedef NSInteger NSComparisonResult;
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

JJD

Posted 2010-10-22T10:30:47.283

Reputation: 24 856

Answers

21

You can define a block as a global variable to get an effect similar to functions.

NSComparisonResult (^globalBlock)(id,id) = ^(id lhs, id rhs) {
    if([lhs intValue] < [rhs intValue]) {
        return (NSComparisonResult)NSOrderedAscending;
    } else if([lhs intValue] > [rhs intValue]) {
        return (NSComparisonResult)NSOrderedDescending;
    }
    return (NSComparisonResult)NSOrderedSame;
};

Then, in the method doing the comparison:

int index1 = [array indexOfObject:number 
                    inSortedRange:NSMakeRange(0, [array count]) 
                          options:NSBinarySearchingFirstEqual 
                  usingComparator:globalBlock]; 

To put the block in a header, for external use:

NSComparisonResult (^globalBlock)(id,id);

ughoavgfhw

Posted 2010-10-22T10:30:47.283

Reputation: 37 056

Can you please add the header declaration to your answer? – JJD – 2011-01-21T09:21:18.247

Please correct the position of the round brace. It has to be like this NSComparisonResult (^MakeComparisonBlock)(id,id) = (^(id lhs, id rhs) { ... });. SO does not allow me to edit a single character. – JJD – 2011-10-10T12:41:59.620

@JJD Actually, those parentheses are unnecessary. I accidentally left them in when converting from inline to global, and older compilers let it go. I'll remove them. – ughoavgfhw – 2011-10-10T14:27:14.720

Nice! Time to grant you the answer flag. I love SO! – JJD – 2011-10-10T16:27:17.593

2

I know this is old, but I just ran across it and I've been trying to work on my blocks foo, so here goes...

I created a method that returns your NSComparator as a block. It looks like this:

-(NSComparisonResult (^) (id lhs, id rhs))compareNSNumber{

return [[^(id lhs, id rhs)
         {
             return [lhs intValue] < [rhs intValue] ? (NSComparisonResult)NSOrderedAscending : [lhs intValue] > [rhs intValue] ? (NSComparisonResult)NSOrderedDescending : (NSComparisonResult)NSOrderedSame;

         } copy ] autorelease];
}

I was then able to run your sample code by changing the binary search execution to:

// Run binary search.
int index1 = [array indexOfObject:number 
                    inSortedRange:NSMakeRange(0, [array count]) 
                          options:NSBinarySearchingFirstEqual 
                  usingComparator:[self compareNSNumber]];
NSTimeInterval stop1 = [NSDate timeIntervalSinceReferenceDate]; 
NSLog(@"Binary: Found index position: %d in %f seconds.", index1, stop1 - start);

I got very similar output to the original implementation with the block definition within the method call.

Adam

Posted 2010-10-22T10:30:47.283

Reputation: 820

I really like your implementation. Though, since I cannot grant 2 correct answers I have to decide on @ughoavgfhw's because I asked for an externally defined objective-c block. No worries! - Interestingly, Xcode adds a colon and one argument on mistake when I autocomplete [self compareNSNumber:id lhs]. – JJD – 2011-10-10T12:50:18.373