苹果GNU C/C++,objective-C/C++新特性:Blocks
作者:CocoaChina会员 zenny_chen。 帖子地址: http://www.cocoachina.com/bbs/read.php?tid-9222.html 现在外面流行一个词,叫多核,呵呵。由于现在硬件工艺达到了饱和,所以很多处理器制造商想从多核
作者:CocoaChina会员 “zenny_chen”。
帖子地址:http://www.cocoachina.com/bbs/read.php?tid-9222.html
现在外面流行一个词,叫“多核”,呵呵。由于现在硬件工艺达到了饱和,所以很多处理器制造商想从多核来进一步发掘处理器的性能。
而Apple现在 也引领了时尚,对XCode 3.2中的GCC 4.2扩充了Blocks新的语法特性,使其能够有助于多核应用的开发。
其中,Apple在 Snow Leopard中所用到的Grand Central Dispatch(GCD)就是基于Blocks实现的。关于Blocks以及GCD在苹果官方的介绍,请见:Introducing Blocks and Grand Central Dispatch。
下面先介绍一下编译器的设置,使其能够认得Blocks 语法。
首先,打开XCode3.2中菜单项Project中的Edit Project Settings。
在 Architectures大栏中的Base SDK项,我们要选择Mac OS X 10.6。
然后看Compiler Version这一栏,我们要将C/C++ Compiler Version项选择为GCC4.2。
最后,在GCC4.2 Language这一栏中,我们要找到C Language Dialect这一项,把它设为GNU99。
这样,编译环境的配制就完成了。
下面我们将开始介绍 Blocks
首先看一段最简单的例子:
|
首先,声明了一个变量void(^blocks)(void),表示一个指向void(void)的函数块的引用。而^(void){ puts("Hello, world!"); };则表示一个函数块,它接受void类型参数,并且返回void。其功能是输出Hello, world!。
下面的blocks();就是通过这个函数块的引用来调用函数。所以,这段代码的运行结果就是输出Hello, world!。
Blocks 比C++0x中的λ表达式更强的一点是,它可以是个数组类型:
|
这 里p的类型为void(^[2])(void),表示含有2个void(^)(void)块引用元素的变量。
下面谈谈函数块对其外部临时 变量的可访问情况。
复制代码
|
对 于FP比较熟悉的朋友可能会想到,如果一个外部变量能够随随便便被一个函数块修改的话,那么对于其本身的副作用仍然无法进行方便地多核并行编程。
那 么我们不妨试试看吧:
|
对 于全局变量可以进行修改,但是对于main函数中的局部变量则不行。如果对local修改则无法通过编译。很显然,Blocks对此已经有了相应的机制。 那么我们如何能够对local进行修改呢?
|
这里引入了一个新的关键字 ——__block,用此声明一个局部变量可以被函数块修改。
我们再来讨论一个更高级的话题,Blocks是否可以递归?
如果要 使Blocks能够递归,那么在函数块中必须能够引用函数块的入口地址。我做了一些尝试,当函数块引用是全局的或static的,即函数块内所引用的函数 块引用变量的值在初始时就已经确定的,那么可以使用递归。
|
如 果在上述代码中将blocks前的static去掉,那么在运行时就会出错,因为blocks在被函数块引用时是未初始化值,所以调用它的话就访问了无效 地址,或者所要执行的指令是未定义的。
各位可以再做些尝试。
下面将详细地分析一下Blocks结合泛型的使用。
由于泛型能够使我们更高效、合理地管理好自己的代码,同时也为部件化提供了许多便利之处。那么 Blocks与泛型结合会产生什么新元素呢?
我们先举一个简单例子:
|
上 述代码中尚未出现Blocks,但是我们可以看到,一般的外部函数能够作为模板参数。
那么Blocks是否可以这么做呢?我们不妨尝试一下:
|
编 译时会在第11行出现error: no matching function for call to \'BlockTest()\'
C++标准 中明确指出,模板参数必须为常量表达式,如果是函数的话必须是带有外部连接(即external-linkage)的函数指针。而Blocks表达式首先 就不是一个常量表达式,然后它也没有外部连接。
我们下面看第二个例子:
|
上 述代码中使用了函数引用作为函数参数,然后由实参类型演绎出模板类型。这段代码将能正常地通过编译、连接并正常运行。
那么我们下面再看一看 Blocks是否具有这个泛型特性:
|
编 译后出现 error: no matching function for call to \'BlockTest(void (^)(int))\'
即 使显式地将<int>模板实参加上也没用。也就是说Blocks的参数类型包括返回类型不能是一个泛型。
好,我们再看第三个 例子:
|
- BlockTest(Hi);
- }
这段代码展示了整个函数指针类型演绎出模板实参。对于目前已被很多编译器所实现的Lambda表达式,这是与泛型挂钩的唯一桥梁,那么Blocks是否具备 这个特性呢?
|
恭 喜,我们成功了。这段代码能够正常编译和运行。各位可以自己看看输出结果。其中,类型信息是被压缩过的:F表示函数,P表示指针,v表示void类型。
Blocks 与C++0x中的lambda表达式一样,必须作为一个完整的类型。对其类型做拆分进行泛型化是非法的。
由于C++0x的Lambda表达式的具体类型不对程序员开放,因此它即不能作为模板形参亦无法作为模板函数的形参,但是它可以在模板函数内使用泛型。
|
上 述代码可以在VS2010β以及Intel C++ Compiler11.0通过编译并正常运行。
然而比较奇怪的是Blocks在模板函数内的 表现就非常不好——
|
上 面这段代码中,模板函数BlockTest中pBlocks根本就没用泛型,也无法通过编译,而且报的错误是internal error: segmentation fault。而只有下面这种情况才能通过编译,但实际上是没有任何意义的:
|
可 见,Blocks作为Lambda表达式而言已经是非常棒的,但与lambda函数功能相比就稍逊一些,尤其是与泛型结合时,表现得很一般。其实这也是与 Blocks的实现有关的。Blocks仍然像objective-C的很多特性那样,主要是靠动态实现的,在编译时所花的精力较少。因此它目前也无法用 于iPhone开发,因为还需要有专门的运行时库。
我想,等到C++0x正式出台后,C++0x中的lambda表达式和lambda函数可能会 用得更多些。然而,能够在C以及objective-C中用上Lambda特性也算是一种福份了,呵呵。

浙公网安备 33010602011771号