IOS 使用Block
调用Block
如果把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当成了inline。你可以将一个block当成任何其他的参数一样,作为一个函数参数传递给函数。然而在大部分情况下,你不需要声明blocks,而只是在blocks需要当成参数时,直接内联实现他们。下面是一个使用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));
该函数为了实现多种调用而采用一个dispatch队列。函数包含3个参数:第一个指定运行的迭代器的数量;第二个指定提交的block对应的队列;第三个就是block本身,该block将依次取出迭代器当前index对应的一个参数。你可以使用dispatch_apply简单的打印出迭代器的index,具体代码为:
#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当成方法的参数
Cocoa可以在很多的方法中使用blocks。你可以将block作为方法参数传递给方法。
下面的例子表示决定出现在给定filter集的一个数组的元素是否是属于前五个成员。
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对象是否包含了指定一个局部变量的单词,如果包含了,那么设置另外一个局部变量(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
一般来说,你不需要复制或者保留block。当你希望使用block时,你只需要在声明block之后的作用域中,复制一份。复制将把block移动到内存堆中。可以使用下面的两个C函数来复制或者释放block。Block_copy();
Block_release();
如果使用Objective-C,你可以给block发送copy、retain
,以及 release
(包含 autorelease
) 消息。
为了避免内存泄露,除非在自动垃圾回收环境,你必须总是在使用Block_copy()之后使用Block_release()。也必须在使用了copy或retain之后使用release(或autorelease)。
必须避免的一些模式:
blcok语法(^{ ... })是一种局部栈数据结构的地址,用来代表block。这种局部栈数据结构的作用域是一个闭合的语句中,因此必须避免使用下面的模式:
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中添加断电来进行单步调试,也可以通过使用invoke-block在GDB会话中调用block,如下面的例子:$ invoke-block myBlock 10 20
如果需要传递一个C类型的string,你必须应用它。例如,给doSomethingWithString传递this string,你必须这么写:$ invoke-block doSomethingWithString "\"this string\""
PS:原文地址http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/bxUsing.html
还可以参考Using Blocks in iOS 4: Designing with Blocks