译自: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