Using Blocks
Invoking a Block
If you declare a block as a variable, you can use it as you would a function, as shown in these two examples:
int (^oneFrom)(int) = ^(int anInt) {
return anInt - 1;
};
printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"
float (^distanceTraveled)(float, float, float) =
^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance;
};
float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9
Frequently, however, you pass a block as the argument to a function or a method. In these cases, you usually create a block “inline”.
调用block
如果你声明一个block变量,你像使用一个函数一样使用它,如下所示:
int (^oneFrom)(int) = ^(int anInt) {
return anInt - 1;
};
printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"
float (^distanceTraveled)(float, float, float) =
^(float startingSpeed, float acceleration, float time) {
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
return distance;
};
float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9
通常情况,你将一个block作为一个参数传递给一个函数(方法)。这时,你可以创建一个内联block。
Using a Block as a Function Argument
You can pass a block as a function argument just as you would any other argument. In many cases, however, you don’t need to declare blocks; instead you simply implement them inline where they’re required as an argument. The following example uses the qsort_b function. qsort_b is similar to the standard qsort_r function, but takes a block as its final argument.
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strncmp(left, right, 1);
});
// Block implementation ends at "}"
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
Notice that the block is contained within the function’s argument list.
The next example shows how to use a block with the dispatch_apply function. dispatch_apply is declared as follows:
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
The function submits a block to a dispatch queue for multiple invocations. It takes three arguments; the first specifies the number of iterations to perform; the second specifies a queue to which the block is submitted; and the third is the block itself, which in turn takes a single argument—the current index of the iteration.
You can use dispatch_apply trivially just to print out the iteration index, as shown:
#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(count, queue, ^(size_t i) {
printf("%u\n", i);
});
使用block作为函数参数
你可以将一个block作为参数传给函数,就像你传递其他参数一个样子。然而,在很多情况下,你不需要对block进行声明;相反,它们就需要作为一个参数,你只需简单的inline实现他们。下面的例子使用了qsort_b函数。qsort_b类似于标准的qsort_r函数,但用block做为最后的参数。
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strncmp(left, right, 1);
});
// Block implementation ends at "}"
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
注意,该block包含在函数的参数列表中。
下面的例子展示了dispatch_apply函数如何使用block。dispatch_apply声明如下:
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
这个函数提交block给多次调用的调度队列(这是啥意思?)。它需要三个参数,第一个指定的迭代次数来执行(迭代);二是指定block提交的队列,第三个是block,而block本身又需要一个单一的参数,指当前的迭代次数。
你可以使用dispatch_apply打印出迭代次数(当前迭代数),如下:
#include <dispatch/dispatch.h>
size_t count = 10;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(count, queue, ^(size_t i) {
printf("%u\n", i);
});
Using a Block as a Method Argument
Cocoa provides a number of methods that use blocks. You pass a block as a method argument just as you would any other argument.
The following example determines the indexes of any of the first five elements in an array that appear in a given filter set.
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
test = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) {
if ([filterSet containsObject: obj]) {
return YES;
}
}
return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
NSLog(@"indexes: %@", indexes);
/*
Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/
The following example determines whether an NSSet object contains a word specified by a local variable and sets the value of another local variable (found) to YES (and stops the search) if it does. Notice that found is also declared as a __block variable, and that the block is defined inline:
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
*stop = YES;
found = YES;
}
}];
// At this point, found == YES
使用block作为Method的参数
Cocoa提供了很多使用block的方法。你把一个block,向其他任何参数一样,作为一个方法的参数。下面的示例确定在给定筛选器集中出现的数组中的五个元素的第一个索引(这一段翻译的很牵强)。
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil];
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop);
test = ^(id obj, NSUInteger idx, BOOL *stop) {
if (idx < 5) {
if ([filterSet containsObject: obj]) {
return YES;
}
}
return NO;
};
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
NSLog(@"indexes: %@", indexes);
/*
Output:
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
*/
下面的示例确定NSSet对象包含一个字一个局部变量中指定的设置另一个本地变量的值(发现)是(停止搜索)如果它。本文由B9班的真高兴发布于CSDN博客注意发现也声明为__block变量,那块定义内联:
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
*stop = YES;
found = YES;
}
}];
// At this point, found == YES
Copying Blocks
Typically, you shouldn’t need to copy (or retain) a block. You only need to make a copy when you expect the block to be used after destruction of the scope within which it was declared. Copying moves a block to the heap.
You can copy and release blocks using C functions:
Block_copy();
Block_release();
To avoid a memory leak, you must always balance a Block_copy() with Block_release().
Copying Blocks
通常情况下,你不需要copy(或retain)一个block。需要你copy一个block的时候是:你希望在block声明的范围被销毁的时候,这个时候你copyblock进入heap(堆)。
你可以使用C方法copy或者release block。
Block_copy();
Block_release();
为了避免内存泄露,你需要成对的写 block_copy() 和block_release
Patterns to Avoid
A block literal (that is, ^{ … }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = ^{ printf("hello, %d\n", i); };
// WRONG: The block literal scope is the "for" loop.
}
}
void dontDoThisEither() {
void (^block)(void);
int i = random():
if (i > 1000) {
block = ^{ printf("got i at: %d\n", i); };
// WRONG: The block literal scope is the "then" clause.
}
// ...
}
避免的写法
一个block字面值(即,{…})表示的是block的本地数据结构stack栈。因此,栈的局部数据结构的范围是封闭的复合语句,因此,您应该避免在下面的示例中的写法:
void dontDoThis() {
void (^blockArray[3])(void); // an array of 3 block references
for (int i = 0; i < 3; ++i) {
blockArray[i] = ^{ printf("hello, %d\n", i); };
// WRONG: The block literal scope is the "for" loop.
// block字面值的作用域是,for循环
}
}
void dontDoThisEither() {
void (^block)(void);
int i = random():
if (i > 1000) {
block = ^{ printf("got i at: %d\n", i); };
// WRONG: The block literal scope is the "then" clause.
// block字面值的作用域是if语句
}
// ...
}
Debugging
You can set breakpoints and single step into blocks. You can invoke a block from within a GDB session using invoke-block, as illustrated in this example:
$ invoke-block myBlock 10 20
If you want to pass in a C string, you must quote it. For example, to pass this string into the doSomethingWithString block, you would write the following:
$ invoke-block doSomethingWithString "\"this string\""
调试
你可以设置断点和单步进入block。你可以调用一个在GDB会话使用的block,如下(?):
$ invoke-block myBlock 10 20
如果你想通过一个C字符串,你必须引用它。例如,把字符串传入dosomethingwithstring block,你会写:
$ invoke-block doSomethingWithString "\"this string\""