`

Block 编程(翻译官方文档)

 
阅读更多

转载请附上原文链接:http://blog.csdn.net/perfect_promise/article/details/7757746

注:小弟才疏学浅,英文水平够烂,若有不正确或误导的地方,请大家指出,欢迎大家指正和修改。本文中涉及的词法范围:作用范围,例如if{}else{},两个{}分别是if和else的作用范围。
介绍
Block对象是一个C级别的语法和运行机制。它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆、栈内存绑定的变量。因此,Block对象包含着一组状态数据,这些数据在程序执行时用于对行为产生影响。
你可以用Block来写一些可以传到API中的函数语句,可选择性地存储,并可以使用多线程。作为一个回调,Block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。
作为Mac OS X v10.6 Xcode开发工具附带的工具,Block在GCC和Clang中同样可用。你能在Mac OS X v10.6 及其以上版本和iOS 4.0及其以上版本中使用Block。.Block的运行是开源的,因此你能在LLVM’s compiler-rt subproject repository里面找到它。Block也已经被提交到C标准工作组作为 N1370: Apple’s Extensions to C。 由于Objective-C 和 C++ 都是衍生自 C,block被设计为可同时兼容这三种语言。
你应该阅读这篇文档,去学习Block是什么,以及在C、C++和OC中如何使用Block使你的程序更加的高效和更易于维护。
声明和使用Block
用^操作符来声明一个Block变量,并指明Block述句的开始。Block的主体部分包含在 {}内,像下面的例子中一样(与C语法一样,“;”指明语句的结束):

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};


image: ../Art/blocks.jpg
注意:Block可以使用定义范围之内的任何变量。

如果你把Block声明为一个变量,你以后就可以像调用一个方法一样使用它:

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

printf("%d", myBlock(3));
// prints "21"
直接使用Block
很多情况下,你不需要声明Block变量;你只是简单地写一个Block语句内联在需要使用它作为参数的地方。下面的例子使用了 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);
});

// myCharacters is now { "Charles Condomine", "George", "TomJohn" }

Cocoa Block
Cocoa框架中有几个方法使用Block作为参数,通常是在执行对象的操作集合,或者操作完成后使用它作为回调。下面的例子向我们展示了

在NSArray 对象的方法sortedArrayUsingComparator:怎样使用Block.。这个方法只有单一的参数,block被定义为NSComparator 局部变量:

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);

/*
Output:
finderSortArray: (
    "string 1",
    "String 02",
    "String 11",
    "string 12",
    "String 21"
)
*/
_block变量
Block的一个强大的特性是,它能在相同的词法范围内修改变量值。Block能修改变量是通过_block存储类型标示符。与 “Cocoa Block” 中的例子一样,你能使用一个block变量来计算有多少字符串与下面例子中是相同的。 Block直接被用,并且用 currentLocale作为一个只读变量在block中。

NSArray *stringsArray = [NSArray arrayWithObjects:
                         @"string 1",
                         @"String 21", // <-
                         @"string 12",
                         @"String 11",
                         @"Strîng 21", // <-
                         @"Striñg 21", // <-
                         @"String 02", nil];

NSLocale *currentLocale = [NSLocale currentLocale];
__block NSUInteger orderedSameCount = 0;

NSArray *diacriticInsensitiveSortArray = [stringsArray sortedArrayUsingComparator:^(id string1, id string2) {

    NSRange string1Range = NSMakeRange(0, [string1 length]);
    NSComparisonResult comparisonResult = [string1 compare:string2 options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];

    if (comparisonResult == NSOrderedSame) {
        orderedSameCount++;
    }
    return comparisonResult;
}];

NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
NSLog(@"orderedSameCount: %d", orderedSameCount);

/*
Output:

diacriticInsensitiveSortArray: (
    "String 02",
    "string 1",
    "String 11",
    "string 12",
    "String 21",
    "Str\U00eeng 21",
    "Stri\U00f1g 21"
)
orderedSameCount: 2
*/
更详细的内容请查看 “Blocks 和变量.”

Block功能
Block是一个匿名的内嵌代码集:

与方法一样,有一个类型参数列表
有一个隐形或声明的返回类型
能从它定义的词法范围内获取状态
能有选择性地修改词法范围中的状态
能共享相同词法范围内其他块定义的修改的潜在性
词法范围被销毁后仍能继续在已定义的词法范围内共享和修改状态
你能复制一个block,并把它传递给其他线程来延迟执行(或者,在它自己的线程内,做一个运行环)。编译和运行过程中,从block中引用的所有变量都保留乐一份block的副本。block不仅适用于纯 C 和 C++,同时block也是一个Objective-C 对象。

用法
Blocks通常表示比较小的,独立的代码段。因此,它特别适用于可能被同时执行的封装单元工作的模式,或者是集合中的项目,或者是当另一个操作完成后的一个回调。

Blocks之所以能替代传统的回调方法主要有以下两个理由:

它允许你在调用点写代码,调用点稍后会在方法实现段被执行。
Blocks通常也是框架方法中的参数。

它允许访问局部变量。
与其使用回调,需要一个包含所有上下文信息的数据结构,你只需要执行一个操作,直接访问局部变量即可。

声明block参考
Block变量持有Block引用。 声明它的语法与在函数中声明指针类似,用 ^代替 *。 其余部分,与C类型系统,具有完全的互操作性。以下时所有有效块的变量声明:

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
Block支持可变参数 (...)参数。 不带任何参数的block,在参数列表中必须指定为void。Block的设计考虑到类型安全,通过提供给编译器全套的元数据来验证block的使用、传递参数到block中和返回值分配。一个块引用可以转换到任意类型的指针,反之亦然。但是,你不能通过*来获得block的值,因而在编译时,block的大小也不能被计算出来。

你可以创建一个block类型,当你在多个地方使用到同一个签名的block时,这种方式时很好的。

typedef float (^MyBlockType)(float, float);

MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;

创建block
用^指明block语句的开始。在它后面的()是参数列表。block的主体部分在{ }里面.。下面的例子定义了一个简单的block,并把先前定义的变量(oneForm)分配给它。

int (^oneFrom)(int);

oneFrom = ^(int anInt) {
    return anInt - 1;
};
如果你不显式声明块表达式的返回值,它可以根据block的内容进行自动匹配。如果返回类型和参数列表都是void,你也可以省略参数列表。 如果存在多个返回语句,应该正确的进行匹配(又需要的话,可以使用类型转换)。

全局block
在文件级别,你可以使用block作为一个全局表达式。

#import <stdio.h>

int GlobalInt = 0;
int (^getGlobalInt)(void) = ^{ return GlobalInt; };

变量类型
block对象的代码中,变量被看成五种不同的方式。

与函数一样,block支持三种标准类型的变量:

全局变量,带有static修饰符的变量
全局函数(不是专门的变量)
局部变量和参数
Blocks也支持其他两种变量类型:

函数级别的_block变量。如有引用块被复制到堆,block中的_block变量是可变的。
const
最后,在一个方法的实现,块可能引用的Objective-C的实例变量—参考“Object and Block Variables.”

以下是在block中使用变量的规则:

可以访问全局变量,包括词法范围内存在的static变量。
可以传参给block,与传参给函数的方式是一样的。
局部词法范围内的堆栈变量被看成时const变量。
他们的值存放在程序内的block语句中。 在嵌套block中,他们的值来自于最近的词法范围内。

声明为_block类型的 局部词法范围内的变量是可改变的。更改的适用范围仅为局部词法范围,包括词法范围内定义的其他block。 在“The __block Storage Type.”中有更详细的描述。
block作用范围内声明的局部变量,与函数中的局部变量一样。
block的每一次调用,都重新生成此变量的新的副本。这些变量可以被转换为const或引用变量在块内的作用域。

下面的例子说明了局部非静态变量的使用:

int x = 123;

void (^printXAndY)(int) = ^(int y) {

    printf("%d %d\n", x, y);
};

printXAndY(456); // prints: 123 456
如上所述,在block内试图分配一个新的x的值将会报错:

int x = 123;

void (^printXAndY)(int) = ^(int y) {

    x = x + y; // error
    printf("%d %d\n", x, y);
};
为了使一个变量在block内部可以被修改,你应该使用_block来修饰这个变量—参考 “The __block Storage Type.”

_block存储类型
在变量前面加上_block类型修饰符,我们可以指定传进来的变量是可变或者可读写的。_block存储与它类似,但是局部变量的寄存器、auto变量和static存储类型之间相互排斥。

_block变量共享变量之间的作用域和块之间的作用域拷贝变量存储范围内。因此,如果block中定义的所有拷贝在框架内的生存超越帧结束(例如,正在排队等待执行),堆栈帧被破坏后存储也将继续存在。在一个给定的词法范围的多个块,可以同时使用共享变量。

作为优化,在堆栈上的块存储块启动就像自身调用一样。如果块被复制,使用Block_copy(或在Objective-C中块发送一个副本),变量将被复制到堆。因此,_block块的地址可以随时更改。

_block变量有两个进一步的限制:他们不能是可变数组,不能包含C99的可变长数组的结构。

下面的例子说明了_block变量的作用:

__block int x = 123; //  x lives in block storage

void (^printXAndY)(int) = ^(int y) {

    x = x + y;
    printf("%d %d\n", x, y);
};
printXAndY(456); // prints: 579 456
// x is now 579
下面的例子显示了几种类型的变量块的相互作用:

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变量
Block提供支持的Objective-C和C+ +的对象,和其他块,作为变量。

Objective-C 对象

在手动引用计数的环境, 复制块时,块内使用局部变量保留。Block内使用的局部变量引用技术将retain。如果您想覆盖一个特定对象变量的这种行为,你可以标记_block修饰符来修饰该变量。

如果您使用ARC,当block被copy时对象变量被保留,并自动释放,和延迟释放。

注:在垃圾收集的环境,如果你给变量同时使用_weak和_block修饰符,那么该block将无法确保是否还存在。
如果你在执行方法内使用block,实例变量对象的内存管理规则更加微妙:

如果您访问实例变量的参照,对象retain;
如果您访问实例变量的值,对象retain;
下面的例子说明了两种不同的情况:

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);
});
调用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作为函数参数
可以把block作为函数参数进行传递,就像其他参数一样。然后,很多时候你不需要声明block;而你只需把他们内联到需要使用block作为一个参数的地方。下面的例子使用了 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函数。 dispatch_apply使用以下方式进行定义:

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
功能是提交一个block到一个调度队列进行多次调用。它携带了三个参数;第一个参数指定执行的迭代的数量;第二个参数指定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);
});
使用block作为方法参数
Cocoa提供了一种方法,使用blocks。传递block作为参数与传递其他参数的方式是一样的。

下面的示例,确定一个数组前五个元素中任意一个在过滤集中的索引数。

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对象中是否包含有局部变量指定的一个单词,如果包含的话,设置另一个局部变量的值为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到堆中。.

你能用C函数来复制和释放block:

Block_copy();
Block_release();
如果你使用Objective-C,block的属性可以使用copy、retain、release和autorelease。

为了避免产生内存泄露,block的copy和retain的使用必须平衡。使用了copy和retain的地方必须进行release(autorelease除外)——除非在一个垃圾收集环境。

避免的模式
块文本(即,^{...})是一个堆栈的本地数据结构的地址块。因此堆栈的本地数据结构的范围是封闭的复合语句,所以你应该避免使用下例中的模式:,

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中你可以设置断点进行单步调试。你可以从调用块内GDB会议调用一个block,如下例所示:

$ invoke-block myBlock 10 20
如果你想传一个C字符串值,你必须使用引用。例如, 把这个字符串传到doSomethingWithString block中,你可以像下面这样写:

$ invoke-block doSomethingWithString "\"this string\""

 

分享到:
评论

相关推荐

    iOS Block编程

    ios block的常用声明, 创建, 定义基本使用规则. 这里是老狼对苹果文档的官方翻译.

    IOS Blocks 编程要点

    block编程 文档 人家转的

    hp8920[编程].pdf

    hp8920[编程]指令说明文档 英文版 Overview of the Test Set The Test Set combines up to 22 separate test instruments and an Instrument BASIC (IBASIC) Controller into one package. All of the Test Set’s...

    编程机器人-mBlock-Mbot.pptx

    基于Makeblock的产品教学,可以将不同领域的知识融合在一起 通 过搭建、编程、手工、实验等活动,提高你的逻辑思维能力、创造能力和问题解决能力。 编程机器人-mBlock-Mbot全文共21页,当前为第4页。 编程机器人-...

    Stm32L0xx编程文档

    The ultra-low-power STM32L072xx are offered in 9 different package typesfrom 32 pins to 100 pins. Depending on the device chosen, ...Figure 1 shows the general block diagram of the device family.

    嵌入式系统 的详细文档

    内容基于一套包含多种智能控制模块的嵌入式实时控制软件,实际上就是一套软PLC软件包,并结合当前工控组态软件实际情况,开发符合国际标准的功能块图(Function Block Diagram—FBD)编程语言[3],即第四代编程语言...

    嵌入式系统详细文档

    内容基于一套包含多种智能控制模块的嵌入式实时控制软件,实际上就是一套软PLC软件包,并结合当前工控组态软件实际情况,开发符合国际标准的功能块图(Function Block Diagram?FBD)编程语言[3],即第四代编程语言...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    4.2.1 DCB(Device Control Block)结构 89 4.2.2 超时设置COMMTIMEOUTS结构 92 4.2.3 OVERLAPPED异步I/O重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 Windows API串行通信函数 97 4.4 ...

    Visual C++/Turbo C串口通信编程实践及源代码-3

    4.2.1 dcb(device control block)结构 89 4.2.2 超时设置commtimeouts结构 92 4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 ...

    GOTC-OpenBlock从少儿编程领域到企业级开发.pdf

    不错的资源哦!

    sun 多线程编程指南

    sun主机经典多线程文档,描述了多线程如何交互、通信,block等一系列的api接口。是其他主机系统多线程的开山书籍

    SPDK官方文档中文版(2019年8月版).pdf

    3.6.18 Virtio Block 47 3.6.19 Virtio SCSI 47 3.7 BlobFS(Blobstore文件系统) 48 3.7.1 RocksDB集成 48 3.7.2 FUSE插件 49 3.8 JSON-RPC方法(略) 49 第四章 程序员指南 49 4.1. Blobstore程序员指南 49 4.1.1 ...

    mbot机器人巡线程序-mBot机器人编程——用机器人的逻辑思考问题.pdf

    mBot是MakeBlock公司⽣产的⾯向⼴⼤青少年的教育机器⼈,它⽀持图形化编程和c语⾔代码编程。其机器⼈⼤脑主板集成了RGB彩灯、 红外接收模块、蜂鸣器、电源模块和红外发射模块等。且包含了直流电件、红外遥控、蓝⽛...

    Visual C++/Turbo C串口通信编程实践及源代码-2

    4.2.1 dcb(device control block)结构 89 4.2.2 超时设置commtimeouts结构 92 4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 ...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    4.2.1 dcb(device control block)结构 89 4.2.2 超时设置commtimeouts结构 92 4.2.3 overlapped异步i/o重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 windows api串行通信函数 97 ...

    visual C++_Turbo C串口通信编程实践

    4.2.1 DCB(Device Control Block)结构 4.2.2超时设置COMMTIMEOUTS结构 4.2.3 OVERLAPPED异步I/O重叠结构 4.2.4 通信错误与通信设备状态 4.2.5 串行通信事件 4.3 Windows API串行通信函数 4.4 Win32 API...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 源码光盘

    4.2.1 DCB(Device Control Block)结构 89 4.2.2 超时设置COMMTIMEOUTS结构 92 4.2.3 OVERLAPPED异步I/O重叠结构 94 4.2.4 通信错误与通信设备状态 95 4.2.5 串行通信事件 96 4.3 Windows API串行通信函数 97 4.4 ...

    Blocks 编程要点

    你应该阅读该文档来掌握 block 对象是什么和如何在 C,C++或 Objective-C 上面使 用它们来让你的程序更高效和更易于维护。

    matlab帅的代码-doctress:一个现代的python识字编程工具

    构建的工具集,以允许一种“陷阱门”文学编程和文档。 源代码使用块名称和结束语句进行注释,并且可以在单个文档中散布散文、测试结果和指标,以呈现为 HTML、LaTeX 或其他格式。 用法 doctress [-o outfile] 在 ...

    UG NX二次开发API.chm

    二次开发中的编程接口的约定,对象、属性、表达式和链表操作;利用UI Styler、Block UI Styler和MenuScript创建用户对话框、菜单和工具条的技术,对话框控件的访问,各种常用对话框的运用;日志录制、编辑、回放以及...

Global site tag (gtag.js) - Google Analytics