Accessing Enclosing Scope
Blocks can also access their enclosing scope. Say you wanted to print out each of the words that contains "badger." You can use a variation of the previous code, using -enumerateObjectsUsingBlock:
[array enumerateObjectsUsingBlock: ^(id object, NSUInteger index, BOOL *stop) { NSRange match = [object rangeOfString: @"badger"]; if (match.location != NSNotFound) { NSLog (@"found a '%@' : %@", @"badger", object); } }];
Which would print out
found a 'badger' : badger found a 'badger' : badgerific
It would be nice to be able to generalize this code so "badger" is not hard-coded. Blocks can capture the values of variables defined in the scope that contains the block. Here is a more general version:
NSString *subString = @"badger"; [array enumerateObjectsUsingBlock: ^(id object, NSUInteger index, BOOL *stop) { NSRange match = [object rangeOfString: subString]; if (match.location != NSNotFound) { NSLog (@"found a '%@' : %@", subString, object); } }];
As you would expect, this also prints out
found a 'badger' : badger found a 'badger' : badgerific
The subString variable does not have to be a hard-coded assignment either. It could be a value retrieved from an NSTextField or UITextField.
So, what is happening here? The compiler emits code that captures the value of any variables in the outer scope that are used inside of the block. The block, in essence, takes a snapshot of the world at this point in time. Captured local variables are treated as constant. You cannot assign to subString inside of the block; if you do, you will get an error.
Captured Objective-C objects are retained. subString therefore has been retained. It will automatically be released when the block goes away.