block的语法

 

主要内容:c++

1. 开始使用block(Getting Started with Blocks)
2. block概念综述(Conceptual Overview)
3. 声明和建立block(Declaring and Creating Blocks)
4. block和变量(Blocks and Variables)
5. 使用blocks(Using Blocks)数组

block介绍

    block对象是C-level语句和运行时的特征,跟标准C函数有些类似;可是除了代码,它能够包含栈变量和堆变量。所以,当执行的时候,block能够维持一组状态(或者数据)。
    开发者能够将block和函数表达式组合,可传递给API或者多线程。block尤为适用于回调,由于block携带了须要执行的的代码和数据。
    block在GCC和Clang都是可用的。开发者可在OS X v10.6或者iOS 4.0以后使用。block runtime是开源代码。在LLVM’s compiler-rt subprojct repository中找到。block也被带入到C标准工程组,做为N1370:Apple’s Extensions to C。由于OC和C++都源自C,因此,block被设计为三种语言下可工做的(还有Objective-C++),其语法反应这一目标。xcode

    开发者阅读这篇文档,了解block是什么,并了解怎么在C、C++和Objective-C使用它。安全


1、开始使用block

下面的几段代码帮助你开始使用block
数据结构

声明并使用block

开发者使用^操做符声明block变量,^表示是一个block的开始。block的body体在{ }以内。例如:多线程

[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. int multiplier = 7;  
  2. int (^myBlock)(int) = ^(int num) {  
  3.     return num * multiplier;  
  4. };  

例以下图所示:app


block可使用“在其定义做用范围内的”变量;若是你声明了一个block变量,能够像函数同样使用它。
框架

[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. int multiplier = 7;  
  2. int (^myBlock)(int) = ^(int num) {  
  3.     return num * multiplier;  
  4. };  
  5.    
  6. printf("%d", myBlock(3));  
  7. // prints "21"  

直接使用block

    多数状况,没必要要声明block变量;相反只须要写一个block代码(或者翻译为文字),在须要参数的地方。下面的例子使用了qsort_b函数。qsort_b跟qsort_r类似的函数,可是使用block做为参数。dom

[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };  
  2.    
  3. qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {  
  4.     charchar *left = *(charchar **)l;  
  5.     charchar *right = *(charchar **)r;  
  6.     return strncmp(left, right, 1);  
  7. });  
  8.    
  9. // myCharacters is now { "Charles Condomine", "George", "TomJohn" }  

Cocoa和block

    好几个Cocoa框架的函数使用block做为参数,一般,block也是集合对象的操做或者当作一个回调。下面代码展现如何在NSArray方法 sortedArrayUsingComparator中使用block。该方法有只一个block参数。如图,block被定义为 NSComparator的本地变量。
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. NSArray *stringsArray = @[ @"string 1",  
  2.                            @"String 21",  
  3.                            @"string 12",  
  4.                            @"String 11",  
  5.                            @"String 02" ];  
  6.    
  7. static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch | NSNumericSearch |  
  8.         NSWidthInsensitiveSearch | NSForcedOrderingSearch;  
  9. NSLocale *currentLocale = [NSLocale currentLocale];  
  10.    
  11. NSComparator finderSortBlock = ^(id string1, id string2) {  
  12.    
  13.     NSRange string1Range = NSMakeRange(0, [string1 length]);  
  14.     return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];  
  15. };  
  16.    
  17. NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];  
  18. NSLog(@"finderSortArray: %@", finderSortArray);  
  19.    
  20. /* 
  21. Output: 
  22. finderSortArray: ( 
  23.     "string 1", 
  24.     "String 02", 
  25.     "String 11", 
  26.     "string 12", 
  27.     "String 21" 
  28. */  

__block变量

    block的一个强大的功能是,它能够修饰词法范围内的变量。开发者能够用__block(两个短下划线)存储类型来修饰变量。该一下代码,你可使用 block变量来计数多少个string被比较。如图,这种状况直接使用block并使用currentLocale做为一个只读变量。
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. NSArray *stringsArray = @[ @"string 1",  
  2.                           @"String 21", // <-  
  3.                           @"string 12",  
  4.                           @"String 11",  
  5.                           @"Strîng 21", // <-  
  6.                           @"Striñg 21", // <-  
  7.                           @"String 02" ];  
  8.    
  9. NSLocale *currentLocale = [NSLocale currentLocale];  
  10. __block NSUInteger orderedSameCount = 0;  
  11.    
  12. NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {  
  13.    
  14.     NSRange string1Range = NSMakeRange(0, [string1 length]);  
  15.     NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];  
  16.    
  17.     if (comparisonResult == NSOrderedSame) {  
  18.         orderedSameCount++;  
  19.     }  
  20.     return comparisonResult;  
  21. }];  
  22.    
  23. NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);  
  24. NSLog(@"orderedSameCount: %d", orderedSameCount);  
  25.    
  26. /* 
  27. Output: 
  28.   
  29. diacriticInsensitiveSortArray: ( 
  30.     "String 02", 
  31.     "string 1", 
  32.     "String 11", 
  33.     "string 12", 
  34.     "String 21", 
  35.     "Str\U00eeng 21", 
  36.     "Stri\U00f1g 21" 
  37. orderedSameCount: 2 
  38. */  
更详细的讨论在下面的【block和变量】。

2、概念综述

    block对象提供一个方法:建立一个ad hoc的功能body体,使用c语言(C派生的语言,好比Objective-C和c++)表达式。在其余的语言和环境中。block对象有时候被称之为“closure”。这里他们一般被称做“block”。

block 功能

block是一个匿名的inline代码集合:
● 参数列表,就像一个函数。
● 有inferred或者声明的返回类型
● 可得到义 词法范围的状态,。
● 可选择性修改词法范围的状态。
● 能够用相同的词法范围内定义的其它block共享进行修改的可能性
● 能够继续共享和修改状态的词法范围内定义的(栈帧)词汇范围(栈帧)已被破坏
开发者能够copy block甚至传递它给其余线程延期执行。编译器核运行时系统整理:block引用的变量会被保护。尽管block在纯C和C++中能够,可是block还是OC对象。

使用

    block至关的小,自包含的代码。所以,他们尤为有用,在封装可能同时执行的工做。 over items in a collection。或者做为其余操做完成的回调。
    block是一个较好得替代传统的callback函数,由于两个缘由:
1. 容许你把写代码写在调用处,根据函数实现的上下文,该处稍后执行。所以,block常常做为框架的参数。
2. 容许访问本地变量。相比于callback须要一个数据结构封装全部上下文,block中能够直接访问本地变量。

3、声明和建立block

声明block引用

    block变量持有block的引用。开发者声明他们使用“定义函数指针类似的”语法,须要用^代替*。block和C系统配合很好。
下面都是有效的声明:
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. void (^blockReturningVoidWithVoidArgument)(void);  
  2. int (^blockReturningIntWithIntAndCharArguments)(int, char);  
  3. void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);  
block仍然支持可变参数(…)。没有参数的block必须指定void在参数列表。
block被设计为全类型安全。开发者能够cast block引用为任意的指针类型,反过来也同样。然而,不能将指针转化成block,所以,block的大小不能在编译时期计算。
开发者为block建立type,这样作一般是最好的作法。当在多个地方须要block签名
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. typedef float (^MyBlockType)(float, float);  
  2.    
  3. MyBlockType myFirstBlock = // ... ;  
  4. MyBlockType mySecondBlock = // ... ;  

建立一个block

    使用^表示block文字表达式的开始。它可能带()的参数列表跟着。block的body体包含在{}。下面例子定义一个简单block并把它赋值给一个提早定义好的变量(oneFrom),已C语句结束。
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. float (^oneFrom)(float);  
  2.    
  3. oneFrom = ^(float aFloat) {  
  4.     float result = aFloat - 1.0;  
  5.     return result;  
  6. };  
若是你不显示声明blcok的返回值,它能够从该block的内容中自动推断。若是返回值被推断,参数列表是void,你也能够忽略(void)参数列表。若是当多个return语句都存在,它们必须彻底匹配(有必要时使用cast)

全局block

At a file level, 开发者可使用block
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. #import <stdio.h>  
  2.    
  3. int GlobalInt = 0;  
  4. int (^getGlobalInt)(void) = ^{ return GlobalInt; };  

4、block和变量


该篇文章描述block和变量的相互做用,包括内存管理。

变量类型

你能够引用三种不一样类型的变量,就像从一个函数同样
● 全局变量,包括静态本地变量
● 全局函数
● 本地变量和参数,从一个封闭的范围
block也支持另外2两个类型:
1. 在功能的级别__block变量。他们是block内可变的和被保留,若是有任何的block被赋值到堆中。
2. const imports
最终,在一个方法的实现中。block可能引用OC的对象变量。参考“Object and Block Variables”
block中的变量,有如下五种不一样的对待,下面的规则使用于block内的变量。
1. 全局变量,能够访问,包括封闭 词法范围的静态变量。
2. 传递到block的参数是可访问的(就行函数的参数同样)。
3. 词法范围内的栈变量 ,被捕获为const变量。它们的值取在程序中的block表达式的地方。在嵌套block,该值是从最近的封闭范围内得到。
4. __block储修饰符声明的局部变量,是可变的。
任何更改都将反映在封闭的词法范围,包括相同封闭的词法范围内定义的任何其余块。
5. 定义在block范围内的本地变量,它的行为很像函数中的局部变。
每一次调用block将拷贝一份局部变量。这些变量能够反过来被用做常量或经过引用在块内的封闭块中的变量。
下面例子阐述了局部变量的用法:
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. int x = 123;  
  2.    
  3. void (^printXAndY)(int) = ^(int y) {  
  4.    
  5.     printf("%d %d\n", x, y);  
  6. };  
  7.    
  8. printXAndY(456); // prints: 123 456  
注意:试图给x赋新值将是错误的
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. int x = 123;  
  2.    
  3. void (^printXAndY)(int) = ^(int y) {  
  4.    
  5.     x = x + y; // error  
  6.     printf("%d %d\n", x, y);  
  7. };  
容许变量在block中可被访问,你可使用__block存储类型修饰符。

__block存储类型

    你能够指定一个imported的变量是能够修改的,经过__block存储类型修饰符。__block存储跟register、auto和static存储类型类似(可是之间互斥),用于局部变量。
    __block变量驻在存储(变量的做用域,全部blocks,在变量做用域范围内声明或者建立Block 之间共享),所以,这个存储将会在栈结束被留下来。
    做为优化,block存储在栈上,就像block自己那样。若是block被拷贝(经过Block_copy或者copy),变量被拷贝到堆。所以__block变量的地址就可能会改变。

__block变量还有两个限制,他们不能是变长数组,不能是结构体。
下面例子阐述了__block变量的使用
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. __block int x = 123; //  x lives in block storage  
  2.    
  3. void (^printXAndY)(int) = ^(int y) {  
  4.    
  5.     x = x + y;  
  6.     printf("%d %d\n", x, y);  
  7. };  
  8. printXAndY(456); // prints: 579 456  
  9. // x is now 579  
下面的例子展现了几种不一样的变量
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. extern NSInteger CounterGlobal;  
  2. static NSInteger CounterStatic;  
  3.    
  4. {  
  5.     NSInteger localCounter = 42;  
  6.     __block char localCharacter;  
  7.    
  8.     void (^aBlock)(void) = ^(void) {  
  9.         ++CounterGlobal;  
  10.         ++CounterStatic;  
  11.         CounterGlobal = localCounter; // localCounter fixed at block creation  
  12.         localCharacter = 'a'; // sets localCharacter in enclosing scope  
  13.     };  
  14.    
  15.     ++localCounter; // unseen by the block  
  16.     localCharacter = 'b';  
  17.    
  18.     aBlock(); // execute the block  
  19.     // localCharacter now 'a'  
  20. }  

对象和block变量

block提供了Objective-C和C++对象的支持,和其余block、变量。

Objective-C 对象

当block被拷贝,它建立了block内变量的强引用。若是你在方法中使用block。
● 若是你访问实例变量经过引用,强引用self
● 若是你访问实例变量经过值,强应用该变量。
下面的例子阐述两种不一样的状况:
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. dispatch_async(queue, ^{  
  2.     // instanceVariable is used by reference, a strong reference is made to self  
  3.     doSomethingWithObject(instanceVariable);  
  4. });  
  5.    
  6.    
  7. id localVariable = instanceVariable;  
  8. dispatch_async(queue, ^{  
  9.     /* 
  10.       localVariable is used by value, a strong reference is made to localVariable 
  11.       (and not to self). 
  12.     */  
  13.     doSomethingWithObject(localVariable);  
  14. });  
要覆盖一个特定的对象变量这个问题,您能够用__block存储类型修饰符标记。

C++对象

一般能够在block中使用C++对象。在成员函数中,成员变量的引用和函数须要经过this显示调用,所以也是可变的。有两个方面的考虑适用,若是一个block被复制
● 若是你有__block存储类的class,其将是一个基于堆栈的C++对象,那么一般的拷贝构造函数。
● 若是您使用任何其余的C++基于堆栈从一个块中的对象,它必须有一个const的拷贝构造函数。在C++对象,而后使用该构造函数复制。

block

当你拷贝一个block,从该block任何引用到其余功能block被复制,若是有必要。整个tree可能被复制。若是你有block变量,你从block中引用一个block,该block将被复制。

5、使用blocks


调用一个block

若是定义一个block变量,你能够将它做为一个函数,以下面的例子:
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. int (^oneFrom)(int) = ^(int anInt) {  
  2.     return anInt - 1;  
  3. };  
  4.    
  5. printf("1 from 10 is %d", oneFrom(10));  
  6. // Prints "1 from 10 is 9"  
  7.    
  8. float (^distanceTraveled)(float, float, float) =  
  9.                          ^(float startingSpeed, float acceleration, float time) {  
  10.    
  11.     float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);  
  12.     return distance;  
  13. };  
  14.    
  15. float howFar = distanceTraveled(0.0, 9.8, 1.0);  
  16. // howFar = 4.9  
一般,能够传递一个block做为函数参数。这些状况,开发者能够建立inline的block。

将block做为函数的参数

开发者传递一个block做为函数参数,就像其余参数同样。多数状况,开发者不用声明一个block。相反你只须要实现inline的block做为参数,在须要的地方。下面的方法qsort_b使用block做为参数。
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };  
  2.    
  3. qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {  
  4.     charchar *left = *(charchar **)l;  
  5.     charchar *right = *(charchar **)r;  
  6.     return strncmp(left, right, 1);  
  7. });  
  8. // Block implementation ends at "}"  
  9.    
  10. // myCharacters is now { "Charles Condomine", "George", "TomJohn" }  
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. </pre></div><div>注意,该block包含在函数的参数列表中。下一个列子展现了在dispatch_apply函数中使用block,dispatch_apply是这么定义的:void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); 该函数提交一个块到调度队列屡次调用,它有三个参数:第一个指定迭代的数量。第二个参数指定block添加到的队列。第三是块自己,这又须要一个参数,当 前迭代的index。您可使用dispatch_apply平凡刚打印出来的迭代指数,以下所 示:</div><div><pre code_snippet_id="114446" snippet_file_name="blog_20131216_17_728540" name="code" class="objc">#include <dispatch/dispatch.h>  
  2. size_t count = 10;  
  3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  4.    
  5. dispatch_apply(count, queue, ^(size_t i) {  
  6.     printf("%u\n", i);  
  7. });  

使用block做为函数参数

Cocoa提供了一系列的使用block的方法。开发者传一个block做为参数,就像其余参数同样。
下面的例子肯定了“数组中的任意五个元素在给定的set中的索引”。

[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];  
  2. NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil nil];  
  3.    
  4. BOOL (^test)(id obj, NSUInteger idx, BOOLBOOL *stop);  
  5.    
  6. test = ^(id obj, NSUInteger idx, BOOLBOOL *stop) {  
  7.    
  8.     if (idx < 5) {  
  9.         if ([filterSet containsObject: obj]) {  
  10.             return YES;  
  11.         }  
  12.     }  
  13.     return NO;  
  14. };  
  15.    
  16. NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];  
  17.    
  18. NSLog(@"indexes: %@", indexes);  
  19.    
  20. /* 
  21. Output: 
  22. indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)] 
  23. */  
下面的例子肯定了NSSet对象包含一个单词,指定本地变量或者被其余变量赋值。注意found也是__block修饰的。
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
  1. __block BOOL found = NO;  
  2. NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil nil];  
  3. NSString *string = @"gamma";  
  4.    
  5. [aSet enumerateObjectsUsingBlock:^(id obj, BOOLBOOL *stop) {  
  6.     if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {  
  7.         *stop = YES;  
  8.         found = YES;  
  9.     }  
  10. }];  
  11.    
  12. // At this point, found == YES  

拷贝block

    通常,开发者不须要copy(或者retain)block。你须要copy当你但愿在声明的范围被析构后仍使用。copy block到堆上。
能够copy和release block用C函数
Block_copy();
Block_release();
为了不内存泄露,开发者必须使用配对使用

Patterns to Avoid

    block的文字段(即,^{…})是一个栈的本地数据结构,它表示该块的地址。所以栈的局部数据结构的范围是封闭的复合语句,因此你应该避免在下面的例子所示的图案:
[objc] view plain copy 在CODE上查看代码片 派生到个人代码片
    1. void dontDoThis() {  
    2.     void (^blockArray[3])(void);  // an array of 3 block references  
    3.    
    4.     for (int i = 0; i < 3; ++i) {  
    5.         blockArray[i] = ^{ printf("hello, %d\n", i); };  
    6.         // WRONG: The block literal scope is the "for" loop.  
    7.     }  
    8. }  
    9.    
    10. void dontDoThisEither() {  
    11.     void (^block)(void);  
    12.    
    13.     int i = random():  
    14.     if (i > 1000) {  
    15.         block = ^{ printf("got i at: %d\n", i); };  
    16.         // WRONG: The block literal scope is the "then" clause.  
    17.     }  
    18.     // ...  
相关文章
相关标签/搜索