Hour 20. Working with Blocks


What You’ll Learn in This Hour

Image Comparing blocks to callbacks

Image Using typedef and function pointers

Image Declaring blocks

Image Using blocks


Revisiting Blocks

Blocks are not new; they were a part of Smalltalk and other languages 30 or more years ago. (In some cases, they were referred to as closures for reasons that you see in this section.) They show up in Ruby, Python, and Lisp as well. For a variety of reasons, they were not implemented on OS X until Snow Leopard (10.6); they have been in iOS since version 4. Because of their long history, for many people using blocks on OS X or iOS, it is a matter of revisiting concepts they might know from other languages or even from long-ago basic programming courses.

Blocks take advantage of one of the most basic principles of computers in the twentieth century; computers store and manipulate digital data. And here is the critical principle: Digital data can represent data in the sense of numbers or text just as easily as it can represent computer instructions. It is all digital, and it is all managed in basically the same way.

GO TO Image Hour 22, “Grand Central Dispatch: Using Queues and Threading,” p. 283, for additional information on blocks.

A block is a chunk of code that can be assigned to a variable or property similarly to the way in which you would assign an int or a struct or an object to a variable or property. You need not assign the chunk of code to a variable or property; you can also reference it in an expression.

Whether assigned to a variable or property or used in an expression, a block can access variables that are defined at the time it is assigned or used. The closure terminology refers to the fact that the block closes around the values of variables at the time it is defined. In this way, those variables differ from arguments that could be passed into a function.

Blocks are frequently used in loops. They are also frequently used in framework methods in much the same way as callback functions have been used in C and its derivative languages. Methods such as enumerateObjectsUsingBlock: in NSArray let you pass in a block as an argument to be used in the enumeration. A similar method in NSSet is used in the same way, whereas in NSDictionary, enumerateKeysAndObjectsUsingBlock: is available. Not to be outdone, NSString has enumerateLinesUsingBlock:.


Note: Finding More on Enumerate Methods Using Blocks

These classes and methods are described more fully later in this hour.


Looking at Callbacks

Blocks are similar in some ways to callback functions, which have long been used in C and Objective-C as well as other programming languages. Callbacks are often used in real-time processing, so you find them in Core Audio and similar frameworks of Cocoa. A callback is typically passed in as a parameter in some code that manages an event that might occur (and recur) over time.

At some point (the end of a movie clip, for example), the process is over, and certain completion code needs to be called. This is a common example of a callback. You pass in a pointer to the completion routine, and it is called at the appropriate moment. Callbacks used in this way are often a more elegant and efficient way than polling the ongoing event to see if it is finished and then performing the completion routine.

GO TO Image Hour 17, “Extending a Class with Protocols and Delegates,” p. 231, for information on delegates, which in some cases can provide similar functionality to blocks.

One way of looking at callback functions is that in some ways they are like supercharged functions. A function can take any number of arguments that you pass in when you call it (as long as they are defined). This goes to the heart of what a function is—code that can function on data that is passed into it at runtime.

Callback functions are functions that can be passed in at runtime, just as is the case with data (again, as long as the definition allows them). Being able to change not only the data on which a function acts, but also the functions that it may call in the course of its processing allows for a great deal of flexibility.

The only tricky part of callback programming is that you need to somehow package a C function in a format that can be passed in as a parameter to the function or method that is going to call it. You do that by passing it in as a function pointer. There are two versions of function pointers:

Image Function pointers using typedef

Image Inline function pointers

The two styles are shown in the following sections.


Tip

Instead of using a typedef, you can use an inline function pointer to achieve the same result. This means a bit less typing (some people would say it is less clear for that reason).


Considering Callback Routines

Callback routines are part of C and many other languages. In C, they are implemented with basic constructs of the language that may be put together in a way that people are unfamiliar with, but there really is nothing new there.

The most important point to remember is that with both the inline function pointer and the typedef declaration, the name of the function is a dummy. You replace it in your use with a function that you declare.

Introducing Blocks

Blocks in Objective-C are implemented differently from callback functions in three important ways:

Image Objective-C blocks are objects at runtime.

Image Blocks can access data from the scope in which they are defined. This means that when a block is called, it has access to the data available where it was defined even if that data is no longer available. The access to this data is read-only.

Image The syntax of a block declaration uses a special character (^), and for many people this makes the block syntax more readable.


Note: Callbacks, Delegates, and Blocks

Blocks have similarities to callback functions. Also, as noted previously in Objective-C, the functionality of callback functions is often implemented using delegates. Bear in mind that these three concepts are related, but they are certainly not identical. In this section, you will see the basics of how to work with blocks. The main example is roughly comparable to the example shown for callbacks, but it is not identical.


Just as with callback functions, you can create a block as a variable or as a typedef; you can also create a block inline. Although it is slightly more verbose, declaring a block as a variable or using a typedef is somewhat clearer, and as you will see, in some cases, it is preferred.

As noted previously, this section is generally similar to the preceding function pointer section.

Creating a Block as a Block Variable

You can declare a block as a variable. The main difference in the syntax is that block declarations use a carat (^) rather than an asterisk, as is the case with pointers. You can declare a block using syntax such as this:

float(^myBlock)(int, float);

This represents a block named myBlock that will take two arguments, an int and a float. It will return a float.

The typedef method shown for function pointers is preferred if you are going to be using this pattern several times. It can put your block declarations together in one place, and the same definition can be used for several blocks. The previous declaration can become a typedef as follows:

typedef float(^MyBlockType)(int, float);

To create a variable, use the typedef just as you ordinarily would, and provide the code for the block:

MyBlockType myBlock = ^(int myInt, float myDivisor) {
  return myInt / myDivisor;
};

Using a Block Variable

Use a block variable just as you would a function:

float myFloat = myBlock (17, 2.0);


Note: Finding Out More About Blocks and Block Syntax

The code in the previous steps is deliberately basic and bare bones. Continue to the next section, “Exploring Blocks in Cocoa,” to see real-life examples of blocks and how they can be used. You also see how to use a block variable as a function argument in Cocoa methods.


Exploring Blocks in Cocoa

Some of the major enumeration methods are described in this section, but you should be aware that they are far from the only enumeration methods in Cocoa that use blocks. Also, these are just the highlights of the methods for some of the classes that implement block-based enumeration. Check the class reference for the classes you are interested in to find more enumeration methods.

All of these methods use the same basic structure: They enumerate over an object that may be a collection object or an object that otherwise consists of discrete entities such as lines in a string. They specify the format of the block that you pass in (that is, its signature). One of the arguments in the block is typically a BOOL named stop. You set this to YES to stop the enumeration at that point.

Here is an overview of the major block enumeration methods in Cocoa. Each has a companion method that includes an options argument. Thus, NSArray has enumerateObjectsUsingBlock: as well as enumerateObjectsWithOptions:usingBlock:.

The options are as follows:

Image NSEnumerationConcurrent—This is taken as a hint but might improve the speed on multicore devices. Your block’s code must be safe against concurrent invocation (that is, it must be able to be performed on two cores at the same time without each copy interfering with the other).

Image NSEnumerationReverse—This is undefined for NSDictionary and NSSet objects because they are unordered. When selected together with NSEnumerationConcurrent, it is also undefined. It is a good idea to stay away from undefined operations. They are not errors, but their behavior is indeterminate.


Tip: You Can Enumerate Small Objects

When you think about enumerating over items, it is reasonable to think about the computer whirring away through collections with hundreds or even thousands of objects in them. That is a reasonable thought, and, particularly with today’s multicore computers running Cocoa-based apps, the speed is impressive.

However, small-scale enumerations are just as valuable, and an argument can be made in favor of using block enumerations over only a single object to be enumerated. The rationale for using small-scale enumerations is that the blocks structure your code and focus your attention very well. Among other things, this means that if at some time in the future you need to do the same thing to 1,000 objects that you do to a single object in a special-case part of your app, the conversion to a block-based enumeration is simple.


NSString enumerateLinesUsingBlock:

This is a frequently used method that enumerates each line in a string and applies a block to it. Note that in this and other interfaces, the actual block code is shown as {...}. Here is the interface:

– (void)enumerateLinesUsingBlock:(void (^)(NSString *line, BOOL *stop)){...}

To help you construct this and the following method calls, here is a typical basic implementation. You can use a block that you have declared as described in the previous section, “Using a Block Variable.” However, just as with function pointers, sometimes an inline declaration instead of a variable is easier and faster to write. It may be less clear to future code maintainers, and if you are using it in several places, the block declaration is preferable.

[myString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) {
  // extract value from line using something like
  // if value is END...
  *stop = YES; }
];

As each line is enumerated, your block is called with that line stripped of its terminators so that you can quickly deal with it. The two typical things that you might want to do are the following:

Image Evaluate the line and possibly store its data in an object or other location.


Tip

If you convert each line to an object, chances are you will be storing them in a collection.


Image Based on your evaluation, you may stop the enumeration.

NSArray enumerateObjectsUsingBlock:

This method starts from the beginning of the array and continues with each item until it reaches the end or until you set the block variable stop to YES.

Note that you get each object in turn back in obj; you also get its index in idx. Based on either value (or something else entirely), you can stop the enumeration.

–    (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL
*stop)){...}

Listing 20.1 shows an example of how you would use that method on an array called myArray:

LISTING 20.1 Using enumterateObjectsUsingBlock:


[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  do something with obj and/or idx
  if (someTest)
    {stop = YES};
}];


NSSet enumerateObjectsUsingBlock:

Sets are not ordered, so if you compare the NSSet method with the NSArray method, you will see that the index is not returned or set:

– (void)enumerateObjectsUsingBlock:(void (^)(id obj, BOOL *stop)) {...}

NSDictionary enumerateKeysAndObjectsUsingBlock:

Dictionaries are not ordered, but for each value there is a key. Thus, the basic enumeration method for NSDictionary looks a bit like the method for NSArray, returning a value and a key for NSDictionary compared with a value and an index for NSArray:

– (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))
{...}

Looking Deeper into Cocoa Blocks and Memory

Blocks are objects that, by default, are allocated on the stack. When execution leaves the scope in which the block is defined, it means they are gone—unless you copy them to the heap.

To copy a block to the heap, declare it as a block variable and then copy with this code:

[myBlock copy];

There are also C functions: Block_copy() and Block_release().

You can use the __block storage type modifier for local variables as in this case:

__block double myDouble;

This places the variable in a storage location that is shared between the normal scope of the variable and all blocks declared or created within that scope. This means that you can import this variable into any of those blocks and modify it if you need to.

Summary

This hour explored blocks and how they are different from C callback functions. You have seen how to create them as the syntax to use them with basic Cocoa objects, such as the enumeration methods for NSArray, NSDictionary, and NSet.

Q&A

Q. What variables can a block reference?

A. A block can reference variables declared as __block in the scope in which the block is declared as well as the arguments passed into it.

Q. What is the preferred way to declare a block?

A. Using a typedef is the preferred way to declare a block. If you are declaring it inline, that can be more concise.

Workshop

Quiz

1. How long can a block be?

2. If you are using a __block variable in a block, what happens when control exits the scope in which the variable is declared?

Quiz Answers

1. There is no hard-and-fast rule, but most blocks are short and to the point.

2. It is still valid in the block until the block is exited.

Activities

Experiment with the enumeration methods described in this chapter, adding NSLog methods to see what is happening as you enumerate and evaluate items in the collections.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.136.142