Blocks

来源:互联网 发布:win7怎么新建网络连接 编辑:程序博客网 时间:2024/04/30 12:43

Blocks

A block is an extension to the C language, introduced in OS X 10.6 and available in iOS 4.0 and later. It’s a way of bundling up some code and handing off that entire bundle as an argument to a C function or Objective-C method. This is similar to what we did in Example 3-1, handing off a pointer to a function as an argument, but instead we’re handing off the codeitself. The latter has some major advantages over the former, which I’ll discuss in a moment.

To illustrate, I’ll rewrite Example 3-1 to use a block instead of a function pointer. Instead of calling sortedArrayUsingFunction:context:, I’ll call sortedArrayUsingComparator:, which takes a block as its parameter.The block is typed like this:

NSComparisonResult (^)(id obj1, id obj2)

That expression is similar to the syntax for specifying the type of a pointer to a function, but a caret is used instead of an asterisk. It denotes a block that takes two id parameters and returns an NSComparisonResult (which is merely an NSInteger, with just the same meaning as in Example 3-1). We can define the block inline as the argument within our call to sortedArrayUsingComparator:, as inExample 3-2.

Example 3-2. Using an inline block instead of a callback function

NSArray* arr2 = [arr sortedArrayUsingComparator: ^(id obj1, id obj2) {

    NSString* s1 = obj1;

    NSString* s2 = obj2;

    NSString* string1end = [s1 substringFromIndex:[s1 length] - 1];

    NSString* string2end = [s2 substringFromIndex:[s2 length] - 1];

    return [string1end compare:string2end];

}];

The syntax of the inline block definition is:

^(id obj1, id obj2) { // ...

}


First, the caret.


Then, parentheses containing the parameters, similar to the parameters of a C function definition.


Finally, curly braces containing the block’s content. These curly braces constitute a scope.


NOTE

The return type in an inline block definition is usually omitted. If included, itfollows the caret,not in parentheses. If omitted, you may have to use typecasting in the return line to make the returned type match the expected type.


A block defined inline, as in Example 3-2, isn’t reusable; if we had two calls to sortedArrayUsingComparator: using the same callback, we’d have to write out the callback in full twice. To avoid such repetition, or simply for clarity, a block can be assigned to a variable, which is then passed as an argument to a method that expects a block, as inExample 3-3.

Example 3-3. Assigning a block to a variable

NSComparisonResult (^sortByLastCharacter)(id, id) =^(id obj1, id obj2) {

    NSString* s1 = obj1;

    NSString* s2 = obj2;

    NSString* string1end = [s1 substringFromIndex:[s1 length] - 1];

    NSString* string2end = [s2 substringFromIndex:[s2 length] - 1];

    return [string1end compare:string2end];

};

NSArray* arr2 = [arr sortedArrayUsingComparator: sortByLastCharacter];

NSArray* arr4 = [arr3 sortedArrayUsingComparator: sortByLastCharacter];

Perhaps the most remarkable feature of blocks is this: variables in scope at the point where a block is defined keep their value within the block at that moment, even though the block may be executed at some later moment. (Technically, we say that a block is a closure, and that variable values outside the block may becaptured by the block.) This aspect of blocks makes them useful for specifying functionality to be executed at some later time, or even in some other thread.

Here’s a real-life example:

CGPoint p = [v center];

CGPoint pOrig = p;

p.x += 100;

void (^anim) (void) = ^{

    [v setCenter: p];

};

void (^after) (BOOL) = ^(BOOL f) {

    [v setCenter: pOrig];

};

NSUInteger opts = UIViewAnimationOptionAutoreverse;

[UIView animateWithDuration:1 delay:0 options:opts

                 animations:anim completion:after];

That code does something quite surprising. The method animateWithDuration:delay:options:animations:completion: configures a view animation using blocks. But the view animation itself is not performed until later; the animation, and therefore the blocks, will be executed at an indeterminate moment in the future, long after the method call has completed and your code has gone on to other things. Now, there is a UIView object v in scope, along with a CGPoint p and another CGPoint pOrig. The variables p and pOrig are local and automatic; they will go out of scope and cease to exist before the animation starts and the blocks are executed. Nevertheless, thevalues of those variables are being used inside the blocks, as parameters of messages to be sent to v.

Because a block might be executed at some later time, it is not normally legal, inside a block, to assign directly to a local automatic variable defined outside the block; the compiler will stop us (“variable is not assignable”):

CGPoint p;

void (^aBlock) (void) = ^{

    p = CGPointMake(1,2); // error

};

A local automatic variable can be made to obey special storage rules by declaring the variable using the __block qualifier.This qualifier promotes the variable’s storage so that the variable stays alive along with the block that refers to it, and has two chief uses.Here’s the first use: If a block will be executed immediately, the __block qualifier permits the block to set a variable outside the block to a value that will be needed after the block has finished.

For example, the NSArray method enumerateObjectsUsingBlock: takes a block and calls it immediately for each element of the array. It is a block-based equivalent of a for...in loop, cycling through the elements of an enumerable collection (Chapter 1). Here, we propose to cycle through the array until we find the value we want; when we find it, we set a variable (dir) to that value. That variable, though, is declaredoutside the block, because we intend to use its valueafter executing the block — we need its scope to extend outside the curly braces of the block. Therefore we qualify the variable’s declaration with __block, so that we can assign to it frominside the block:

CGFloat h = newHeading.magneticHeading;

__block NSString* dir = @"N";

NSArray* cards = @[@"N", @"NE", @"E", @"SE",

                  @"S", @"SW", @"W", @"NW"];

[cards enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

    if (h < 45.0/2.0 + 45*idx) {

        dir = obj;

        *stop = YES;

    }

}];

// now we can use dir

(Note also the assignment to a dereferenced pointer-to-BOOL. This is a way of interrupting the loop prematurely; we have found the value we’re looking for, so there’s no point looping any further. We can’t use a break statement, because this isn’t really a for loop. The method enumerateObjectsUsingBlock: therefore hands the block a pointer-to-BOOL parameter, which the block can set by indirection to YES as a signal to the method that it’s time to stop. This is one of the few situations in iOS programming where it is necessary to dereference a pointer.)

The second chief use of the __block qualifier is the converse of the first. It arises when a block will be executed at some time after it is defined, and we want the block to use the value that a variable has at the time the block is executed, rather than capturing the value that it has when the block is defined. Typically this is because the very same method call that accepts the block (for later execution) also sets the value of this variable (now).

For example, the method beginBackgroundTaskWithExpirationHandler: takes a block to be executed at some future time (if ever). It also generates and returns a UIBackgroundTaskIdentifier, which is really just an integer — and we are going to want to use that integer inside the block, if and when the block is executed. So we’re trying to do things in an oddly circular order: the block is handed as an argument to the method, the method is called, the method returns a value, and the block uses that value. The __block qualifier makes this possible:

__block UIBackgroundTaskIdentifier bti =

    [[UIApplication sharedApplication]

        beginBackgroundTaskWithExpirationHandler: ^{

            [[UIApplication sharedApplication] endBackgroundTask:bti];

        }];

At the same time that blocks were introduced into Objective-C, Apple introduced a system library of C functions called Grand Central Dispatch (GCD) that makes heavy use of them. GCD’s chief purpose is thread management, but it also comes in handy for expressing neatly and compactly certain notions about when code should be executed. For example, GCD can help us delay execution of our code (delayed performance). The following code uses a block to say, “change the bounds of the UIView v1, but not right this moment — wait two seconds and then do it”:

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    CGRect r = [v1 bounds];

    r.size.width += 40;

    r.size.height -= 50;

    [v1 setBounds: r];

});

This final example of a block in action rewrites the code from the end ofChapter 1, where a class method vends a singleton object. The GCD function dispatch_once, which is very fast and (unlike theChapter 1 example) thread-safe, promises that its block, here creating the singleton object, will execute only once in the entire life of our program — thus guaranteeing that the singletonis a singleton:

+ (CardPainter*) sharedPainter {

    static CardPainter* sp = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        sp = [CardPainter new];

    });

    return sp;

}

The block is able to assign to the local variable sp without a __block qualifier because sp has a static qualifier, which has the same effect but even stronger: just as __block promotes the lifetime of its variable to live as long as the block, static promotes the lifetime of its variable to live as long as the program itself. Thus it also has the side-effect of making sp assignable from within the block, just as __block would do.

For further explanation of blocks, see Apple’s helpful documentation athttp://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/, or look up “Blocks Programming Topics” in Xcode’s help window. For a complete technical syntax specification for blocks, seehttp://clang.llvm.org/docs/BlockLanguageSpec.html.


原创粉丝点击