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.
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];
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With