iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) (87 page)

Read iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) Online

Authors: Aaron Hillegass,Joe Conway

Tags: #COM051370, #Big Nerd Ranch Guides, #iPhone / iPad Programming

BOOK: iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides)
12.21Mb size Format: txt, pdf, ePub
Bronze Challenge: Finding the Subforum

When applying the regular expression to each item’s title, capture the subforum name, too. Display the subforum name in the
detailTextLabel
of the cell.

 
Silver Challenge: Swapping the Master Button

When in portrait mode, the
UIBarButtonItem
that contains a popover with the master view controller will show up on the detail view controller’s navigation bar. If you switch from the
ChannelViewController
to the
WebViewController
, you won’t see the bar button item anymore. Fix this so that the bar button item stays visible when switching between the two different detail view controllers in portrait mode. (The bar button item should never appear in landscape mode.)

 
Silver Challenge: Processing the Reply

Forum post titles start with
Re:
when they are a response to another post. Remove the
Re:
from these post titles.

 
Gold Challenge: Showing Threads

After handling the medium challenge, make it so posts that have a
Re:
in them have a pointer to their parent post. In the table, hide all child posts and show them when the disclosure indicator is tapped on the parent post. (If the parent post doesn’t show up in the RSS feed, make the earliest post in the thread the parent.)

 
27
Blocks

In this chapter, you will learn about a C and Objective-C language feature called blocks. A
block
is a set of instructions. It is an object, but it is called like a function. Blocks provide conveniences for a programmer and performance boosts for applications. If you are familiar with other high-level languages, you may know blocks as
closures
or
anonymous functions
. This chapter will introduce blocks and their syntax. In the next chapter, you will put blocks to use in your
Nerdfeed
application.

 
Blocks and Block Syntax

In the C programming language, we separate code into functions. A function is defined as its own entity...

 
int adder(int a, int b)
{
    return a + b;
}

and can be called anywhere. A function can take arguments (input) and return a value (output).

 

In Objective-C, we separate code into classes and then further into methods that a class implements. The syntax of defining and calling a method is different than a function, but really, they are the same thing: chunks of executable code that take arguments and return single values.

 
- (int)addValue:(int)a andValue:(int)b
{
    return a + b;
}

The big difference between a function and a method is that a method can only be executed by an object or class: this is the conceptual difference between procedural programming and object-oriented programming.

 

Blocks are an interesting combination of object-oriented and procedural programming. A block, like a method or function, is a chunk of executable code that can take arguments and return a value. But a block is also an object. This odd combination gives us new design patterns that weren’t easily achievable when limited to functions and objects and their methods. Like the hurdle you had to jump to first understand object-oriented programming, blocks require a similar (although less intense) leap in your understanding of programming.

 

Let’s create a new project to learn more about blocks and their syntax. Create a new iOS
Empty Application
template named
Blocky
and configure it as shown in
Figure 27.1
.

 

Figure 27.1  Configuring Blocky

 
 
Declaring block variables

Blocks, because they are objects, can be pointed to by variables. Think about variable declarations we’ve seen before:

 
NSString *string;

This pointer variable has a name and a type. Its name is
string
, and its type is
NSString *
. The type of a pointer identifies the class of the instance it will point to.

 

A variable that points at a block (a
block variable
) has the same two pieces: a name and a type. The type of a block variable identifies the arguments and the return value of the block it will point to. Because there are more components in a block variable’s type, declaring a block variable requires a different syntax:

 
int (^adder)(int a, int, b);
 

This is what a block variable looks like. The name of this variable is
adder
, and it will point to a block that takes two
int
arguments and returns an
int
. The caret symbol (
^
) is what distinguishes the variable as a pointer to a block – just like an asterisk (*) indicates that a variable is a pointer to an object. Here are more examples of block variables that point to different types of blocks:

 
// A block variable named foo; it can point to a block that takes no arguments
// and returns no value:
void (^foo)(void);
// A block variable named jump; it can point to a block that takes one Hurdle *
// and returns no value:
void (^jump)(Hurdle *);
// A block variable named doubler; it can point to a block that takes one int
// and returns an int:
int (^doubler)(int);

Notice that the syntax is like declaring a C function except that, instead of a function name, there is a variable name prefixed with a caret and wrapped in parentheses.

 

In
BNRAppDelegate.m
, add a local block variable to
application:didFinishLaunchingWithOptions:
.

 
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    
int (^adder)(int, int);
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

This declares a variable named
adder
that will point to a block that returns an
int
and takes two
int
s as arguments. Build the application. You should see a single warning saying that the variable
adder
isn’t used. (If you see anything else, check your syntax and build again.)

 
Defining block literals

Of course, we know that a variable itself doesn’t contain much information: it just stores a value. For example, the value of an
int
variable is a numerical value, like
5
. The value of an
NSString *
variable will be an address of an instance of
NSString
.

 

The same holds true for a block variable. The variable stores a value, and the value will be the address of a block. A block is an object, so it needs to be allocated. To allocate a block, you specify the code that makes up the block. The syntax for creating a block looks like this:

 
^int(int x, int y) {
    return x + y;
};

The definition of a block is called a
block literal
. Notice the form of a block literal: a caret, the return type, and then parentheses that contain the arguments. In this case, this block takes two
int
arguments and returns their sum.

 

Typically, you create a block and immediately assign a variable to point to it. (You can also create a block and then pass it directly to a method. We’ll talk more about this option later in the chapter.) In
BNRAppDelegate.m
, update
application:didFinishLaunchingWithOptions:
to assign
adder
to point to a block object.

 
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    int (^adder)(int, int)
= ^int(int x, int y) {
        return x + y;
    }
;
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

This code is just a combination of what you know: it creates a block variable named
adder
that points to a newly allocated block that returns the sum of the two arguments. Build the application to check for syntax errors. You should still only see the warning that
adder
is unused.

 

Here are some more examples of blocks and variables that point to them:

 
void (^foo)(void) = ^void(void) {
    NSLog(@"I'm a lonely block.");
};
void (^jump)(Hurdle *) = ^void(Hurdle *hurdle) {
    [hurdle comeCrashingDown];
};
int (^doubler)(int) = ^int(int x) {
    return x * 2;
};
 
Executing blocks

Once a block variable points to a block, you can call the variable like a C function to execute the code in the block that is pointed to. Update
application:didFinishLaunchingWithOptions:
to execute the
adder
block and print out its return value.

 
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    int (^adder)(int, int) = ^int(int x, int y) {
        return x + y;
    };
    
int sum = adder(2, 5);
    NSLog(@"%d", sum);
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

Build and run this application. You will see the standard warning about applications not having a root view controller. Ignore this and look for the result of executing this block:
7
.

 
More notes about blocks

There are a few things to mention here. First, notice that a block variable is not required to declare argument names. The block literal will declare the names of its arguments because it is the one that uses them. A block variable can declare the names of its arguments, but those names are irrelevant in the block itself. So, here are some legal block and block variable pairings:

 
void (^foo)(int) = ^void(int x) {
    NSLog(@"%d", x);
};
// Since the block literal calls its argument x, it doesn't matter
// what the block variable calls it.
void (^foo)(int y) = ^void(int x) {
    NSLog(@"%d", x);
};
 

This block and block variable pairing, on the other hand, would cause an error.

 
// This is a compiler error because the block calls its argument x, but it references
// the variable y, which doesn't exist
void (^foo)(int y) = ^void(int x) {
    NSLog(@"%d", y);
};
 

Furthermore, the type of a block variable must match the type of the block it points to. If not, the compiler will give you an error. Here are some examples of erroneous block and block variable pairings:

 
// This is a compiler error because the return types differ (void vs. int)
void (^block)(void) = ^int(void) {
    return 0;
};
// This is a compiler error because the argument types differ (int vs. float)
void (^block)(int) = ^void(float x) {
    NSLog(@"%f", x);
};
 

Also, notice that blocks are created inside methods (or functions). This is different than the definition of methods and functions, which occupy their own area in an implementation file. While it is possible to create a block outside of a method or function, there is rarely any reason to do so.

 

Finally, functions and methods can be called anywhere as long as you have imported the file that contains their declaration. A block, being an object, can only be used if you have a variable that points to it. Therefore, if you create a block in a method, any other method that wishes to execute that block must be passed a pointer to that block. You will test this behavior in the next section.

 

Other books

I Have Landed by Stephen Jay Gould
Hotel World by Ali Smith
Architects Are Here by Michael Winter
Marine Summer: Year 2041 by B. E. Wilson