Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compare blocks and functions in objective C

As I am learning objective C, my understanding is new and incomplete. The concept of a block is very similar to a function. They even look almost identical:

FUNCTION named 'multiply'

#import <Foundation/Foundation.h>

int multiply (int x, int y)
{

    return x * y;

}

int main(int argc, char *argv[]) {
    @autoreleasepool {

        int result = multiply(7, 4); // Result is 28.
        NSLog(@"this is the result %u",result);

        }
}

BLOCK named 'Multiply'

#import <Foundation/Foundation.h>

int (^Multiply)(int, int) = ^(int num1, int num2) {

    return num1 * num2;
};

int main(int argc, char *argv[]) {

    @autoreleasepool {

        int result = Multiply(7, 4); // Result is 28.
        NSLog(@"this is the result %u",result);
    }
}

I found various statements on the web like: "Blocks are implemented as Objective-C objects, except they can be put on the stack, so they don't necessarily have to be malloc'd (if you retain a reference to a block, it will be copied onto the heap, though). "

Ray Wenderlich says: "Blocks are first-class functions"

I have no clue what all this means. My example shows that the same thing is accomplished as a block or a function. Can someone show an example where blocks can do something functions cannot? or vice versa? Or is it something more subtle, like the way the variable 'result' is handled in memory? or is one faster/safer? Can either of them be used as a method in a class definition?

Thank you.

like image 799
aquagremlin Avatar asked Sep 08 '14 01:09

aquagremlin


2 Answers

Blocks are Objective-C objects, and functions aren't. In practice, this means you can pass around a block from one piece of code to another like so:

NSArray *names = @[@"Bob", @"Alice"];
[names enumerateObjectsUsingBlock:^(id name, NSUInteger idx, BOOL *stop) {
    NSLog(@"Hello, %@", name);
}];

In C, you can achieve similar effects by passing around pointers to functions. The main difference between doing this and using blocks, however, is that blocks can capture values. For instance, in the example above, if we wanted to use a variable greeting:

NSString *greeting = @"Hello";
NSArray *names = @[@"Bob", @"Alice"];
[names enumerateObjectsUsingBlock:^(id name, NSUInteger idx, BOOL *stop) {
    NSLog(@"%@, %@", greeting, name);
}];

In this example, the compiler can see that the block depends on the local variable greeting and will "capture" the value of greeting and store it along with the block (in this case, that means retaining and storing a pointer to an NSString). Wherever the block ends up getting used (in this case, within the implementation of [NSArray -enumerateObjectsUsingBlock:]), it will have access to the greetings variable as it was at the time the block was declared. This lets you use any local variables in the scope of your block without having to worry about passing them into the block.

To do the same using function pointers in C, greeting would have to be passed in as a variable. However, this can't happen because the caller (in this case, NSArray) can't know (especially at compile time) exactly which arguments it has to pass to your function. Even if it did, you'd need to somehow pass the value of greeting to NSArray, along with every other local variable you wanted to use, which would get hairy really quickly:

void greet(NSString *greeting, NSString *name) {
    NSLog(@"%@, %@", greeting, name);
}

// NSArray couldn't actually implement this
NSString *greeting = @"Hello";
NSArray *names = @[@"Bob", @"Alice"];
[names enumerateObjectsUsingFunction:greet withGreeting:greeting];
like image 94
igul222 Avatar answered Oct 13 '22 19:10

igul222


Blocks are closures -- they can capture local variables from the surrounding scope. This is the big difference between blocks (and anonymous functions in other modern languages) and functions in C.

Here's an example of a higher-order function, makeAdder, which creates and returns an "adder", a function which adds a certain base number to its argument. This base number is set by the argument to makeAdder. So makeAdder can return different "adders" with different behavior:

typedef int (^IntFunc)(int);

IntFunc makeAdder(int x) {
    return ^(int y) { return x + y; }
}

IntFunc adder3 = makeAdder(3);
IntFund adder5 = makeAdder(5);

adder3(4); // returns 7
adder5(4); // returns 9
adder3(2); // returns 5

This would not be possible to do with function pointers in C, because each function pointer must point to an actual function in the code, of which there is a finite number fixed at compile time, and each function's behavior is fixed at compile time. So the ability to create a virtually unlimited number of potential "adders" depending on a value at runtime, like makeAdder does, is not possible. You would instead need to create a structure to hold the state.

A block which does not capture local variables from the surrounding scope, like in your example, is not much different from a plain function, aside from the type.

like image 37
newacct Avatar answered Oct 13 '22 19:10

newacct