Block是C语言的一个语法特性,同时也是C语言的运行时特性,它很像C中的函数指针,因为你可以像使用函数指针一样的去使用block对象;它也很像C++中的函数对象,因为除了要执行的代码,block还可以携带和block绑定的状态信息。
因此,block是一个对象,这个对象里包含了要执行的代码片段以及一些状态信息。
MacOSX 10.6和iOS 4.0以上版本的Xcode开发包提供了对block的支持。
block是一片具有以下特性的内联代码片段集合:
作为一个自包含的代码片段,由于以下特性,block很适合作为回调函数的替代方案:
你可以通过^操作符定义一个block类型的变量,用{}来圈定block的代码片段,如下图所示:
再次声明:block可以访问和block定义在同一个词法范围里的变量。
int multiplier = 7; int (^myBlock)(int) = ^(int num) { return num * multipiler; } printf("%d", myBlock(3)); // prints "21"
在更多的时候,你并不需要定义自己的Block类型,而是在API中直接编写block代码片段,例如:qsort_b。
char *myCharacter[3] = { "safari", "ie", "chrome" }; qsort_b(myCharacter, 3, sizeof(char *), ^(const void *l, const void *r) { char *left = *(char **)l; char *right = *(char **)r; return strncmp(left, right, 1); });
Cocoa framework中很多方法使用了Block作为其参数(尽管也有对应的callback版本,但还是推荐使用 block版本)。在动画以及集合遍历方面,block很常见。
NSArray *stringsArray = [NSArray arrayWithObjects: @"string 1", @"String 21", @"string 12", @"String 11", @"String 02", nil]; static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |NSWidthInsensitiveSearch | NSForcedOrderingSearch; NSLocale *currentLocale = [NSLocale currentLocale]; NSComparator finderSortBlock = ^(id string1, id string2) { NSRange string1Range = NSMakeRange(0, [string1 length]); return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale]; }; NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock]; NSLog(@"finderSortArray: %@", finderSortArray);
这一部分涉及内存管理相关的内容,为了正确的使用block,理解并记住它们,很重要:
在一个block代码片段的内部,你可以使用三种不同类型的变量(就像你在函数里一样):
当在一个Block里使用变量时,应遵循以下规则:
局部变量的值,取最内层词法范围里,局部变量的值;
正确的:
__block int x = 123; void (^printXandY)(int) = ^(int y) { printf("%d %d\n", x, y); }; printXandY(456);
错误的:
int x = 123; void (^printXandY)(int) = ^(int y) { x = x + y; // ERROR HERE!!! x should be __block printf("%d %d\n", x, y); }; printXandY(456);
下面这个例子用于展示,各种类型的变量与__block之间的交互:
extern NSInteger CounterGlobal; static NSInteger CounterStatic; { NSInteger localCounter = 42; __block char localCharacter; void (^aBlock)(void) = ^(void) { ++CounterGlobal; ++CounterStatic; CounterGlobal = localCounter; // localCounter fixed at block creation localCharacter = 'a'; // sets localCharacter in enclosing scope }; ++localCounter; // unseen by the block localCharacter = 'b'; aBlock(); // execute the block // localCharacter now 'a' }
如果,你在一个方法的实现里,使用了__block,则:
dispatch_async(queue, ^{ // instanceVariable is used by reference, self is retained doSomethingWithObject(instanceVariable); });
id localVariable = instanceVariable; dispatch_async(queue, ^{ // localVariable is used by value, localVariable is retained (not self) doSomethingWithObject(localVariable); });
需要注意两点: