译自:Using Blocks
翻译不不到位之处,请指正,建议点击上面链接看苹果官网介绍。
1. 调用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。
2. 将block作为方法的参数
你可以将block像其他参数一样作为函数参数传递。但是在这种情况下,你没有必要声明blocks,只需要在它需要作为参数的地方内联实现它。下面的例子使用了 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));
The function submits a block to a dispatch queue for multiple invocations. 它有三个参数:第一个表示执行的迭代次数,第二个参数指定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);
});
3. 将block作为函数的参数
cocoa 提供了使用blocks的许多函数。你可以像其他任何参数一样将block作为函数参数传递。下面的这个例子的作用是:数组中前5个元素中的任何一个元素,如果在给定过滤器中出现过,则返回该元素的index。
NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C", @"A", @"B", @"Z",@"G", @"are", @"Q", nil];
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 对象中。如果该单词包含在NSSet对象中则停止搜索,并设定另一局部变量(found)为 YES;注意found是定义为__block类型的,并且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
4. 复制 blocks
通常情况你不必 copy 或者 retain 一个 block。只有当你需要在定义该 block 的作用域被销毁之后继续使用block时,对 block 进行copy。copy 操作会将block移动到堆上。你可以使用C函数进行拷贝和释放 blocks :
Block_copy();
Block_release();
如果你使用 Objective-C, 你可以向 block 发送 copy, retain, release,autorelease消息。
为了避免内存泄露,必须总是保持 Block_copy() 和 Block_release() 成对出现,或 保持 copy 或者 retain 和 release(autorelease)成对出现,除非使用了垃圾回收机制。
5. 应避免的形式
block literal(^{ . . . })是代表block的堆栈数据结构的地址。堆栈数据结构的作用域是闭合的复合语句(the enclosing compound statement),所以你应该避免下面例子中所显示的形式:
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
}
// ...
}
6. 调试
可以设定断电并且单步调试跳进 block, 可以在gdb调试会话时使用 invoke-block 调用block,如下所示:
$ invoke-block myBlock 10 20
如果你想传递 C 字符串, 必须使用双引号括起来。例如,如果你想向 doSomethingWithString 传递 this string 参数时,可以像下面所示传递参数:
$ invoke-block doSomethingWithString "\"this string\""
最后,欢迎大家远离我的微博:http://weibo.com/caryaliu