Here are some other things you can do with blocks.
The block that you created for VowelMovement does not have a return value, but many blocks will. When a block returns a value, you can get the return value by calling the block variable like a function.
Let’s look again at one of the sample blocks you saw at the beginning of the chapter:
^(double dividend, double divisor) { double quotient = dividend / divisor; return quotient; }
This block takes two doubles and returns a double. To store this block in a variable, you would declare a variable of that type and assign the block to it:
// Declare divBlock variable double (^divBlock)(double,double); // Assign block to variable divBlock = ^(double dividend, double divisor) { double quotient = dividend / divisor; return quotient; }
You can then call divBlock like a function to get its return value:
double myQuotient = divBlock(42.0, 12.5);
An anonymous block is a block that you pass directly to a method without assigning it to a block variable first.
Let’s consider the case of an anonymous integer first. When you want to pass an integer to a method, you have three options:
// Option 1: Totally break it down int i; i = 5; NSNumber *num = [NSNumber numberWithInt:i]; // Option 2: Declare and assign on one line int i = 5; NSNumber *num = [NSNumber numberWithInt:i]; // Option 3: Skip the variable entirely NSNumber *num = [NSNumber numberWithInt:5];
If you take the third option, you are passing the integer anonymously. It is anonymous because it does not have a name (or a variable) associated with it.
You have the same options when you want to pass a block to a method. Currently, your code puts the block declaration, assignment, and usage on three separate lines of code. But it is more common to pass blocks anonymously. The first challenge at the end of this chapter is to modify the VowelMovement program to use an anonymous block.
A block typically uses other variables (both primitive variables and pointers to objects) that were created outside of the block. These are called external variables. To make sure that they will be available for as long as the block needs them, these variables are captured by the block.
For primitive variables, the values are copied and stored as local variables within the block. For pointers, the block will keep a strong reference to the objects it references. This means that any objects referred to by the block are guaranteed to live as long as the block itself. (If you have been wondering about the difference between blocks and function pointers, it is right here. Let’s see a function pointer do that!)
If you need to write a block that uses self, you must take a couple of extra steps to avoid a strong reference cycle. Consider an example where an instance of BNREmployee creates a block that will log the BNREmployee instance each time it executes:
myBlock = ^{ NSLog(@"Employee: %@", self); };
The BNREmployee instance has a pointer to a block (myBlock). The block captures self, so it has a pointer back to the BNREmployee instance. You have a strong reference cycle.
To break the strong reference cycle, you declare a __weak pointer outside the block that points to self. Then you can use this pointer inside the block instead of self:
__weak BNREmployee *weakSelf = self; // a weak reference myBlock = ^{ NSLog(@"Employee: %@", weakSelf); };
The block’s reference to the BNREmployee instance is now a weak one, and the strong reference cycle is broken.
However, because the reference is weak, the object that self points to could be deallocated while the block is executing.
You can eliminate this risk by creating a strong local reference to self inside the block:
__weak BNREmployee *weakSelf = self; // a weak reference myBlock = ^{ BNREmployee *innerSelf = weakSelf; // a block-local strong reference NSLog(@"Employee: %@", innerSelf); };
By creating the strong innerSelf reference, you have again created a strong reference cycle between the block and the BNREmployee instance. But because the innerSelf reference is local to the scope of the block, the strong reference cycle will only exist while the block is executing and will be broken automatically when the block ends.
This is good programming practice whenever you write a block that must reference self.
If you use an instance variable directly within a block, the block will capture self instead of the instance variable. This is because of a little-known nuance of instance variables. Consider this code that accesses an instance variable directly:
__weak BNREmployee *weakSelf = self; myBlock = ^{ BNREmployee *innerSelf = weakSelf; // a block-local strong reference NSLog(@"Employee: %@", innerSelf); NSLog(@"Employee ID: %d", _employeeID); };
The compiler interprets the direct variable access like this:
__weak BNREmployee *weakSelf = self; myBlock = ^{ BNREmployee *innerSelf = weakSelf; // a block-local strong reference NSLog(@"Employee: %@", innerSelf); NSLog(@"Employee ID: %d", self->_employeeID); };
Does the -> syntax look familiar? It is the syntax for accessing the member of a struct on the heap. At their deepest darkest cores, objects are actually structs.
Because the compiler reads _employeeID
as self->_employeeID
, self is unexpectedly captured by the block.
This will cause the same strong reference cycle that you avoided with the use of weakSelf and innerSelf.
The fix? Don’t access instance variables directly. Use your accessors!
__weak BNREmployee *weakSelf = self; myBlock = ^{ BNREmployee *innerSelf = weakSelf; // a block-local strong reference NSLog(@"Employee: %@", innerSelf); NSLog(@"Employee ID: %d", innerSelf.employeeID); };
Now there is no direct use of self, so there is no unintentional strong reference cycle. Problem solved.
In this situation, it is important to understand what the compiler is thinking to avoid the hidden strong reference cycle. However, you should never use the -> syntax to access an object’s instance variables in your code. Doing so is dangerous for reasons beyond the scope of this book. Accessors are your friends, and you should use them.
By default, variables captured by a block are constant within the block, and you cannot change their values. If you want to be able to modify an external variable within a block, you must declare the external variable using the __block keyword.
For instance, in the following code, you increment the external variable counter within the block.
__block int counter = 0; void (^counterBlock)() = ^{ counter++; }; ... counterBlock(); // Increments counter to 1 counterBlock(); // Increments counter to 2
Without the __block keyword, you would get a compilation error.