Getting return values from a code block
The block
I've never used any async stuff during my time as a developer, so when I came across blocks in Objective-C I was stumped for a bit as to how they work. Why can't I access any local fields or return this value!. Now that I have had some exposure I'm more familiar with them and understand how they work. I still have the same issue though; there are times I need to get a value back out of the block. Yes I can post to NSNotificationCenter, but I'd rather not.
I learned you can create an instance of a code block, in which case you can return a value and capture it. As an example, let's say we want to build an array of buttons currently referenced by our view controllers view. I know that you wouldn't really need a block to do something like this. In the real-world I would probably just use the [NSArray enumerateObjectsUsingBlock:];
method for the purpose of demonstrating return values in blocks though, it'll work.
Method Signature
We start off by declaring our block. We know we want to return all of the UIButton's stored in the View right? So we need a return value of NSArray. There's no need for any parameters so we will build a block with the following signature.
NSArray *(^fetchAllButtons)(void);
You can typedef this in your header file if you want, or you can make this declaration in the same method that you're going to invoke the block in. It is up to you. If you choose to typedef it, you can do so like this:
typedef NSArray *(^fetchAllButtons)(void);
The ^
in the parentheses indicates that we are creating a code block. The code block will have the name provided within the parentheses. In this case, our block will be called fetchAllButtons. The (void)
indicates that the method takes no arguments and the NSArray *
tells us that the method will return an array.
Implementing the block
Now that we have our block method defined, we need to implement it. This is pretty straight forward once you get used to it, but much like defining the method signature, the syntax can be confusing at first. Let's implement it.
// capture a reference to our view controller View subviews
NSArray *subviews = self.view.subviews;
// Implement the block and assign it to a private property.
self.fetchBlock = ^NSArray *(void) {
// Create our Mutable array
NSMutableArray *buttons = [[NSMutableArray alloc] init];
// Itterate through the subviews and add any buttons to our mutable array
for (UIButton *button in subviews) {
[buttons addObject:button];
}
// Return a immutable copy.
return [buttons copy];
};
Alright, you have now fully implemented a code block that returns a value. The initial assignment can be confusing, assigning ^NSArray *(void)
to a instance property, but once you get passed that, the code looks just like a method.
We created a block of code that is assigned the the self.fetchBlock
property which was declared like this:
@property (nonatomic, copy) fetchAllButtons fetchBlock;
Once we implement the block code above and assign it to the fetchBlock
property, we now have a re-usable chunk of code. A complete method that can be invoked anywhere in our View Controller asynchronously and return a value.
Using the block
Now it's time to actually use it. The definition of the block was put in the header file and the implementation I placed in my viewDidLoad
method. Once that was done, I set up an outlet from a UIButton in my Storyboard to my View Controller and invoked the code block like such.
- (IBAction)fetchAllButtons:(UIButton *)sender {
NSArray *buttons = self.fetchBlock();
NSLog(@"I found %d buttons", [buttons count]);
}
In the above code, we instance a new NSArray and assign it the return value of our code block, which we invoked. Pretty nice right?