主要内容:

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++),其语法反应这一目标。

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

一、开始使用block

下面的几段代码帮助你开始使用block

声明并使用block

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

  1. int multiplier = 7;
  2. int (^myBlock)(int) = ^(int num) {
  3. return num * multiplier;
  4. };

例如下图所示:

block可以使用“在其定义作用范围内的”变量;如果你声明了一个block变量,可以像函数一样使用它。

  1. int multiplier = 7;
  2. int (^myBlock)(int) = ^(int num) {
  3. return num * multiplier;
  4. };
  5. printf("%d", myBlock(3));
  6. // prints "21"

直接使用block

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

  1. charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
  2. qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {
  3. charchar *left = *(charchar **)l;
  4. charchar *right = *(charchar **)r;
  5. return strncmp(left, right, 1);
  6. });
  7. // myCharacters is now { "Charles Condomine", "George", "TomJohn" }

Cocoa和block

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

__block变量

   
block的一个强大的功能是,它可以修饰词法范围内的变量。开发者可以用__block(两个短下划线)存储类型来修饰变量。该一下代码,你可以使用
block变量来计数多少个string被比较。如图,这种情况直接使用block并使用currentLocale作为一个只读变量。
  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. NSLocale *currentLocale = [NSLocale currentLocale];
  9. __block NSUInteger orderedSameCount = 0;
  10. NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {
  11. NSRange string1Range = NSMakeRange(0, [string1 length]);
  12. NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
  13. if (comparisonResult == NSOrderedSame) {
  14. orderedSameCount++;
  15. }
  16. return comparisonResult;
  17. }];
  18. NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
  19. NSLog(@"orderedSameCount: %d", orderedSameCount);
  20. /*
  21. Output:
  22. diacriticInsensitiveSortArray: (
  23. "String 02",
  24. "string 1",
  25. "String 11",
  26. "string 12",
  27. "String 21",
  28. "Str\U00eeng 21",
  29. "Stri\U00f1g 21"
  30. )
  31. orderedSameCount: 2
  32. */

更详细的讨论在下面的【block和变量】。

二、概念综述

    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中可以直接访问本地变量。

三、声明和创建block

声明block引用

    block变量持有block的引用。开发者声明他们使用“定义函数指针相似的”语法,需要用^代替*。block和C系统配合很好。
下面都是有效的声明:
  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签名
  1. typedef float (^MyBlockType)(float, float);
  2. MyBlockType myFirstBlock = // ... ;
  3. MyBlockType mySecondBlock = // ... ;

创建一个block

使用^表示block文字表达式的开始。它可能带()的参数列表跟着。block的body体包含在{}。下面例子定义一个简单block并把它赋值给一个提前定义好的变量(oneFrom),已C语句结束。

  1. float (^oneFrom)(float);
  2. oneFrom = ^(float aFloat) {
  3. float result = aFloat - 1.0;
  4. return result;
  5. };

如果你不显示声明blcok的返回值,它可以从该block的内容中自动推断。如果返回值被推断,参数列表是void,你也可以忽略(void)参数列表。如果当多个return语句都存在,它们必须完全匹配(有必要时使用cast)

全局block

At a file level, 开发者可以使用block

  1. #import <stdio.h>
  2. int GlobalInt = 0;
  3. int (^getGlobalInt)(void) = ^{ return GlobalInt; };

四、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将拷贝一份局部变量。这些变量可以反过来被用作常量或通过引用在块内的封闭块中的变量。
下面例子阐述了局部变量的用法:

  1. int x = 123;
  2. void (^printXAndY)(int) = ^(int y) {
  3. printf("%d %d\n", x, y);
  4. };
  5. printXAndY(456); // prints: 123 456

注意:试图给x赋新值将是错误的

  1. int x = 123;
  2. void (^printXAndY)(int) = ^(int y) {
  3. x = x + y; // error
  4. printf("%d %d\n", x, y);
  5. };

允许变量在block中可被访问,你可以使用__block存储类型修饰符。

__block存储类型

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

__block变量还有两个限制,他们不能是变长数组,不能是结构体。
下面例子阐述了__block变量的使用

  1. __block int x = 123; //  x lives in block storage
  2. void (^printXAndY)(int) = ^(int y) {
  3. x = x + y;
  4. printf("%d %d\n", x, y);
  5. };
  6. printXAndY(456); // prints: 579 456
  7. // x is now 579

下面的例子展示了几种不同的变量

  1. extern NSInteger CounterGlobal;
  2. static NSInteger CounterStatic;
  3. {
  4. NSInteger localCounter = 42;
  5. __block char localCharacter;
  6. void (^aBlock)(void) = ^(void) {
  7. ++CounterGlobal;
  8. ++CounterStatic;
  9. CounterGlobal = localCounter; // localCounter fixed at block creation
  10. localCharacter = 'a'; // sets localCharacter in enclosing scope
  11. };
  12. ++localCounter; // unseen by the block
  13. localCharacter = 'b';
  14. aBlock(); // execute the block
  15. // localCharacter now 'a'
  16. }

对象和block变量

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

Objective-C 对象

当block被拷贝,它创建了block内变量的强引用。如果你在方法中使用block。
● 如果你访问实例变量通过引用,强引用self
● 如果你访问实例变量通过值,强应用该变量。
下面的例子阐述两种不同的情况:

  1. dispatch_async(queue, ^{
  2. // instanceVariable is used by reference, a strong reference is made to self
  3. doSomethingWithObject(instanceVariable);
  4. });
  5. id localVariable = instanceVariable;
  6. dispatch_async(queue, ^{
  7. /*
  8. localVariable is used by value, a strong reference is made to localVariable
  9. (and not to self).
  10. */
  11. doSomethingWithObject(localVariable);
  12. });

要覆盖一个特定的对象变量这个问题,您可以用__block存储类型修饰符标记。

C++对象

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

block

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

五、使用blocks

调用一个block

如果定义一个block变量,你可以将它作为一个函数,如下面的例子:

  1. int (^oneFrom)(int) = ^(int anInt) {
  2. return anInt - 1;
  3. };
  4. printf("1 from 10 is %d", oneFrom(10));
  5. // Prints "1 from 10 is 9"
  6. float (^distanceTraveled)(float, float, float) =
  7. ^(float startingSpeed, float acceleration, float time) {
  8. float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
  9. return distance;
  10. };
  11. float howFar = distanceTraveled(0.0, 9.8, 1.0);
  12. // howFar = 4.9

通常,可以传递一个block作为函数参数。这些情况,开发者可以创建inline的block。

将block作为函数的参数

开发者传递一个block作为函数参数,就像其他参数一样。多数情况,开发者不用声明一个block。相反你只需要实现inline的block作为参数,在需要的地方。下面的方法qsort_b使用block作为参数。

  1. charchar *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
  2. qsort_b(myCharacters, 3, sizeof(charchar *), ^(const voidvoid *l, const voidvoid *r) {
  3. charchar *left = *(charchar **)l;
  4. charchar *right = *(charchar **)r;
  5. return strncmp(left, right, 1);
  6. });
  7. // Block implementation ends at "}"
  8. // myCharacters is now { "Charles Condomine", "George", "TomJohn" }
  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. dispatch_apply(count, queue, ^(size_t i) {
  5. printf("%u\n", i);
  6. });

使用block作为函数参数

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

  1. NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];
  2. NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil nil];
  3. BOOL (^test)(id obj, NSUInteger idx, BOOLBOOL *stop);
  4. test = ^(id obj, NSUInteger idx, BOOLBOOL *stop) {
  5. if (idx < 5) {
  6. if ([filterSet containsObject: obj]) {
  7. return YES;
  8. }
  9. }
  10. return NO;
  11. };
  12. NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test];
  13. NSLog(@"indexes: %@", indexes);
  14. /*
  15. Output:
  16. indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
  17. */

下面的例子确定了NSSet对象包含一个单词,指定本地变量或者被其他变量赋值。注意found也是__block修饰的。

  1. __block BOOL found = NO;
  2. NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil nil];
  3. NSString *string = @"gamma";
  4. [aSet enumerateObjectsUsingBlock:^(id obj, BOOLBOOL *stop) {
  5. if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
  6. *stop = YES;
  7. found = YES;
  8. }
  9. }];
  10. // At this point, found == YES

拷贝block

一般,开发者不需要copy(或者retain)block。你需要copy当你希望在声明的范围被析构后仍使用。copy block到堆上。
可以copy和release block用C函数
Block_copy();
Block_release();
为了避免内存泄露,开发者必须使用配对使用

Patterns to Avoid

block的文字段(即,^{…})是一个栈的本地数据结构,它表示该块的地址。因此栈的局部数据结构的范围是封闭的复合语句,所以你应该避免在下面的例子所示的图案:

    1. void dontDoThis() {
    2. void (^blockArray[3])(void);  // an array of 3 block references
    3. for (int i = 0; i < 3; ++i) {
    4. blockArray[i] = ^{ printf("hello, %d\n", i); };
    5. // WRONG: The block literal scope is the "for" loop.
    6. }
    7. }
    8. void dontDoThisEither() {
    9. void (^block)(void);
    10. int i = random():
    11. if (i > 1000) {
    12. block = ^{ printf("got i at: %d\n", i); };
    13. // WRONG: The block literal scope is the "then" clause.
    14. }
    15. // ...
    16. }

block的语法的更多相关文章

  1. Objective-C中的block块语法

    #import <Foundation/Foundation.h> /* OC不同于Java C++ 它没有字符串对象 只有NS对象 NS就是乔布斯第一个公司NeXt的类集合 被收购之后才 ...

  2. Objective-C中Block语法、Block使用以及通过Block实现数组排序

    Block:语法块,本质上是匿名函数(没有名称的函数) 标准C里面没有Block,C语言的后期扩展版本,加入了匿名函数 在C++.JS.Swift等语言有类似语法,叫做闭包 Block语法和C语言里的 ...

  3. iOS开发——语法OC篇&Block回顾

    Block回顾 这里知识简单介绍一下关于Block的语法,如果你觉得这里很简单或者想学习更深入的的使用清查看记者之前写的使用block传值和高级Block使用: http://www.cnblogs. ...

  4. Block 语法

    Block,代码块,^符号是block的语法标记. 比如说,一个block的参数列表是一个UIView,返回值是个CGFloat,block名称是testBlock 可以定义为  CGFloat (^ ...

  5. block使用小结、在arc中使用block、如何防止循环引用

    引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...

  6. 浅谈 block(1) – clang 改写后的 block 结构

    这几天为了巩固知识,从 iOS 的各个知识点开始学习,希望自己对每一个知识理解的更加深入的了解.这次来分享一下 block 的学习笔记. block 简介 block 被当做扩展特性而被加入 GCC ...

  7. 用typedef自定义类型语法你真的会吗

    关于typedef  我们学习typedef的时候,他的定义语法是:typedef+类型+别名,但是按照上面的格式,自定义数组怎么定义呢,是这样tepedef int a[10] 别名?还是这样tep ...

  8. Objective-C Block

    OC中block的语法实在是太别扭了,不知道是不是我太孤陋寡闻了...在此记录一下,以供日后查找使用. 引用:http://goshdarnblocksyntax.com As a local var ...

  9. block的内部实现

    主要内容: 一.block相关的题目 二.block的定义 三.block的实现 四.捕获自动变量值 五.block存储区域 六.截获对象 一.block相关的题目 这是一篇比较长的博文,前部分是bl ...

随机推荐

  1. 2016年12月22日 星期四 --出埃及记 Exodus 21:17

    2016年12月22日 星期四 --出埃及记 Exodus 21:17 "Anyone who curses his father or mother must be put to deat ...

  2. 红字差评系列1.第K小数

    [题目分析] 二分答案?smg,我太弱了 //不开longlong wa到挺了 #include <cstdio> #include <cstring> #include &l ...

  3. 管道函数(%>%)很简单

    %>%来自dplyr包的管道函数,其作用是将前一步的结果直接传参给下一步的函数,从而省略了中间的赋值步骤,可以大量减少内存中的对象,节省内存 符号%>%,这是管道操作,其意思是将%> ...

  4. [问题2014S10] 解答

    [问题2014S10]  解答 先证明一个简单的引理. 引理  设 \(\lambda_0\) 是 \(n\) 阶方阵 \(A\) 的特征值, 则对任意的正整数 \(k\), Jordan 块 \(J ...

  5. pip 添加trusted host 一劳永逸

    继上一篇<Python virtualenv安装库报错SSL: CERTIFICATE_VERIFY_FAILED>出现的问题进行一个一劳永逸的操作 pip升级到7.0以后,在使用http ...

  6. Java开发中经典的小实例-(比较输入数值的最大值、最小值和平均值)

    //输入数字个数来产生数字并且比较大小 import java.util.Scanner;public class Test1 {     public static void main(String ...

  7. PHP中的闭包和匿名函数

    闭包的概念是指在创建闭包时,闭包会封装周围的状态的函数.即便闭包所在环境不在了.但闭包中封装的状态依然存在. 匿名函数就是没有名称的函数. 它们看似很函数一样,实际上它们属于Closure类的实例 P ...

  8. 如何将php的错误输出到nginx的error_log中去

    参考文档:http://www.cnblogs.com/glory-jzx/p/3966082.html 通过FastCGI运行的PHP,在用户访问时出现错误,会首先写入到PHP的errorlog中如 ...

  9. Android studio环境搭建

    首先要下载jdk,下载好以后配置环境变量,这里略过,不会的可以百度搜索,这里附上jdk下载地址:http://pan.baidu.com/s/1Xrmqy 下载Android studio 软件,下载 ...

  10. h.APR通道是个怎么回事

    APR通道是Tomcat比较有特色的通道,在早期的JDK的NIO框架不成熟的时候,因为java的网络包的低效,Tomcat使用APR开源项目做网络IO,这样有效的缓解了java语言的不足,提供了一个高 ...