- 浏览: 407532 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (325)
- 神经网络 (1)
- javascript (11)
- 数据结构 (2)
- 计算机图形学 (11)
- 模式识别 (1)
- 前端开发 (14)
- 机器学习 (11)
- ios开发 (50)
- Python (9)
- HTML5 (4)
- 计算机视觉 (9)
- 数字图像处理 (7)
- 架构设计 (19)
- 数据库设计 (9)
- 算法设计 (59)
- Java (37)
- 其他 (3)
- 游戏开发 (5)
- c++ (17)
- Linux (3)
- TCP/IP (2)
- Flex (41)
- 健康 (6)
- AI (2)
- 工具 (1)
- 数据挖掘 (1)
- 性能优化 (6)
- 综合 (2)
- 网络通信 (12)
- Android (2)
- UML (3)
- 软件设计 (11)
- 编程经验 (7)
- J2EE (1)
- 多媒体技术 (3)
- 数学 (7)
- php (4)
- 设计 (1)
- CS (2)
- 计算机理论 (1)
- 信息安全 (1)
最新评论
-
ahead_zhan:
good good good
flex3控件_ModuleLoader -
lonerzf:
好样的。非常感谢楼主
OpenCV视频教程整理 -
lonerzf:
好样的。谢谢~
OpenCV视频教程整理 -
coding1688:
博主说的不错,我在实现瀑布流布局时也用的masonry插件,有 ...
Javascript 瀑布流式布局及其动态效果的实现 -
snowolf:
除非玩游戏,不然没啥win的事情,或者用win的银行客户端,通 ...
macbook安装操作系统的机理分析
Objective-C的Block
Apple在C,Objective-C和C++中扩充了Block这种文法的,并且在GCC4.2中进行了支持。现在我们可以在Mac 10.6和iOS 4中使用。如果是Mac 10.6 或 iOS 4.0 之前的平台,据说可以用http://code.google.com/p/plblocks/这个项目来支持Block语法。
Apple在 Snow Leopard中所用到的Grand Central Dispatch(GCD)就是基于Blocks实现的。Grand Central Dispatch是苹果开发的工具,目的是帮助开发者更容易的利用多核处理器的并行处理功能。关于Blocks以及GCD在苹果官方的介绍,请见:Introducing Blocks and Grand Central Dispatch。
你可以把它理解为函数指针,匿名函数,闭包,lambda表达式,这里暂且用块对象来表述,因为它们之间还是有些许不同的。
块对象
块对象是C级别的文法,同时也是一种运行时特征,即它允许您把函数表达式组合在一起,组合结果可作为参数传递也可保存,还可供多个线程使用。块对象的函数表达式可引用或持有局部变量。在其他的语言环境中,块对象有时也被称为closure或者lambda。如果您需创建可如数值般传递的工作单元(即代码段),则可使用块对象,它可为您提供更多的编程灵活性和更强大的功能。如需编写回调函数或对某个群体的所有项执行某种操作,也可使用块对象。
l 声明一个块
如果以内联方式使用块对象,则无需声明。块对象声明语法与函数指针声明语法相似,但是块对象应使用脱字符(^)而非星号指针 (*)。下面的代码声明一个aBlock变量,它标识一个需传入三个参数并具有float返回值的块。
float (^aBlock)(const int*, int, float);
l 创建一个块
块使用脱字符(^)作为起始标志,使用分号作为结束标志。下面的例子声明一个简单块,并且将其赋给之前声明的block变量(oneFrom)。
int (^oneFrom)(int);
oneFrom = ^(int anInt) {
return anInt - 1;
};
结尾处的分号是标准C的行结束标志。如果未显式声明块表达式的返回值,则编译器会根据块内容自动进行推导。
l 块可变变量
如果某个局部变量使用__block存储修饰符,则表示块应使用此变量的引用,并可更改它的值。对变量的任何改变都只在块的语法作用域内部,以及该作用域中定义的其它块中起作用。
块对象的特性
Blocks比C++0x中的lambda表达式更强的一点是,它可以是个数组类型:
int main(void)
{
void (^p[2])(void) = { ^(void){ puts("Hello, world!"); }, ^(void){ puts("Goodbye!"); } };
p[0](), p[1]();
}
这里p的类型为void(^[2])(void),表示含有2个void(^)(void)块引用元素的变量。下面谈谈函数块对其外部临时变量的可访问情况。
static int global = 100;
int main(void)
{
int local = 200;
void (^p)(void) = ^(void){ printf("The answer is: %dn", global + local); };
p();
}
对于FP比较熟悉的朋友可能会想到,如果一个外部变量能够随随便便被一个函数块修改的话,那么对于其本身的副作用仍然无法进行方便地多核并行编程。那么我们不妨试试看吧:
static int global = 100;
int main(void)
{
int local = 200;
void (^p)(void) = ^(void){
printf("The answer is: %dn", global + local);
global++;
local--; // compiling error:error: decrement of read-only variable \'local\'
};
p();
printf("After modified, global is: %d and local is %dn", global, local);
}
对于全局变量可以进行修改,但是对于main函数中的局部变量则不行。如果对local修改则无法通过编译。很显然,Blocks对此已经有了相应的机制。 那么我们如何能够对local进行修改呢?
static int global = 100;
int main(void)
{
__block int local = 200;
static int s = 10;
void (^p)(void) = ^(void){
printf("The answer is: %dn", global + local);
global++;
s++;
local--;
};
p();
printf("After modified, global is: %d and local is %d and s is: %dn", global, local, s);
}
这里引入了一个新的关键字 ——__block,用此声明一个局部变量可以被函数块修改。
Block是否可以递归
如果要使Blocks能够递归,那么在函数块中必须能够引用函数块的入口地址。我做了一些尝试,当函数块引用是全局的或static的,即函数块内所引用的函数 块引用变量的值在初始时就已经确定的,那么可以使用递归。
int main(void)
{
void (^p)(int) = 0;
static void (^ const blocks)(int) = ^(int i){ if(i > 0){ puts("Hello, world!"); blocks(i - 1); } };
p = blocks;
p(2);
}
如果在上述代码中将blocks前的static去掉,那么在运行时就会出错,因为blocks在被函数块引用时是未初始化值,所以调用它的话就访问了无效地址,或者所要执行的指令是未定义的。
Blocks结合泛型
由于泛型能够使我们更高效、合理地管理好自己的代码,同时也为部件化提供了许多便利之处。那么Blocks与泛型结合会产生什么新元素呢?
我们先举一个简单例子:
#import <Foundation/Foundation.h>
template <void pBlock(void)>
void BlockTest(void)
{
pBlock();
}
void Hi(void)
{
NSLog(@"Hi, there!");
}
int main(int argc, const char* argv[])
{
BlockTest<Hi>();
}
上述代码中尚未出现Blocks,但是我们可以看到,一般的外部函数能够作为模板参数。那么Blocks是否可以这么做呢?我们不妨尝试一下:
#import <Foundation/Foundation.h>
template <void (^pBlock)(void)>
void BlockTest(void)
{
pBlock();
}
int main(int argc, const char* argv[])
{
BlockTest<^(void) { NSLog(@"Hi, there!"); }>();
}
编译时会在第11行出现error: no matching function for call to \'BlockTest()\'。C++标准中明确指出,模板参数必须为常量表达式,如果是函数的话必须是带有外部连接(即external-linkage)的函数指针。而Blocks表达式首先 就不是一个常量表达式,然后它也没有外部连接。我们下面看第二个例子:
#import <Foundation/Foundation.h>
template <typename T>
void BlockTest(void (&pBlock)(T))
{
pBlock(T());
}
static void Hi(int a)
{
NSLog(@"The value is: %dn", a);
}
int main(int argc, const char* argv[])
{
BlockTest(Hi);
}
上述代码中使用了函数引用作为函数参数,然后由实参类型演绎出模板类型。这段代码将能正常地通过编译、连接并正常运行。那么我们下面再看一看 Blocks是否具有这个泛型特性:
#import <Foundation/Foundation.h>
template <typename T>
void BlockTest(void (^pBlock)(T))
{
pBlock(T());
}
int main(int argc, const char* argv[])
{
BlockTest(^(int a) { NSLog(@"The value is: %dn", a); });
}
编译后出现 error: no matching function for call to \'BlockTest(void (^)(int))\'。即使显式地将<int>模板实参加上也没用。也就是说Blocks的参数类型包括返回类型不能是一个泛型。我们再看第三个例子:
#import <Foundation/Foundation.h>
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
void BlockTest(T pBlock)
{
pBlock();
cout << "The type is: " << typeid(T).name() << endl;
}
static void Hi(void)
{
NSLog(@"Hi, there!");
}
int main(int argc, const char* argv[])
{
BlockTest(Hi);
}
这段代码展示了整个函数指针类型演绎出模板实参。对于目前已被很多编译器所实现的Lambda表达式,这是与泛型挂钩的唯一桥梁,那么Blocks是否具备这个特性呢?
#import <Foundation/Foundation.h>
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
void BlockTest(T pBlock)
{
pBlock();
cout << "The type is: " << typeid(T).name() << endl;
}
int main(int argc, const char* argv[])
{
BlockTest(^(void) { NSLog(@"Hi, there!"); });
}
恭喜,我们成功了。这段代码能够正常编译和运行。各位可以自己看看输出结果。其中,类型信息是被压缩过的:F表示函数,P表示指针,v表示void类型。Blocks 与C++0x中的lambda表达式一样,必须作为一个完整的类型。对其类型做拆分进行泛型化是非法的。由于C++0x的Lambda表达式的具体类型不对程序员开放,因此它即不能作为模板形参亦无法作为模板函数的形参,但是它可以在模板函数内使用泛型。
#include <iostream>
using namespace std;
template <typename T>
void LambdaTest(T)
{
T a = 100;
auto ref = [a](const T& b) -> T { return a + b; };
cout << "The value is: " << ref(200) << endl;
}
int main(void)
{
LambdaTest((short)0);
}
上述代码可以在VS2010以及Intel C++ Compiler11.0通过编译并正常运行。然而比较奇怪的是Blocks在模板函数内的表现就非常不好——
template <typename T>
void BlockTest(void)
{
void (^pBlocks)(void) = ^{};
}
int main(int argc, const char* argv[])
{
BlockTest<void>();
}
上面这段代码中,模板函数BlockTest中pBlocks根本就没用泛型,也无法通过编译,而且报的错误是internal error: segmentation fault。而只有下面这种情况才能通过编译,但实际上是没有任何意义的:
#import <Foundation/Foundation.h>
template <typename T>
void BlockTest(void)
{
void (^pBlock)(void) = nil;
}
int main(int argc, const char* argv[])
{
BlockTest<void>();
}
可见,Blocks作为Lambda表达式而言已经是非常棒的,但与lambda函数功能相比就稍逊一些,尤其是与泛型结合时,表现得很一般。其实这也是与 Blocks的实现有关的。Blocks仍然像Objective-C的很多特性那样,主要是靠动态实现的,在编译时所花的精力较少。能够在C以及Objective-C中用上Lambda特性也很不错。
小结
Blocks在作用域,内存管理等方面具有自己独特的特性和需要留意的地方,在许多场景也很多实用的功能。虽然有时理解起来有点费劲,但是用好了,相信用好了可以大大提高代码可读性和效率等。
本文出自 “ArthurChen” 博客,请务必保留此出处http://arthurchen.blog.51cto.com/2483760/616735
发表评论
-
IVEngine —— Objective-C的Spring
2013-12-15 20:35 0背景: 只要是Java程序员,相信都听说 ... -
XCode环境变量及路径设置
2013-11-17 16:05 1773转自:http://blog.csdn.net/freedo ... -
OVGap — 沟通 Objective-C 与 Javascript
2013-11-14 15:14 815Github 地址: https://github.com ... -
IOS开发常用命令
2013-10-11 10:41 642统计代码行数 打开terminal, cd到项目根目 ... -
提高在Xcode上的工作效率
2013-08-31 11:05 1101转自:http://liuminqian.github.io ... -
把ipa包上传到AppStore
2013-08-26 21:46 927转自:http://doc.appcan.cn/dev_io ... -
iPhone应用程序中App目录结构
2013-08-26 17:30 962转自:http://blog.csdn.net/justin ... -
iOS运行回路(RunLoop)总结
2013-08-05 13:37 746转自:http://www.cocoachina.com/i ... -
Simple iPhone Keychain Access
2013-08-04 15:54 741转自:http://useyourloaf.com/b ... -
IOS媒体层
2013-07-28 15:18 844转自:http://wd397.blog.163.com/b ... -
nginx上搭建HLS流媒体服务器
2013-07-28 13:31 1387转自:http://blog.csdn.net/ ... -
Compile ffmpeg for iOS 6, support Simulator & armv7 & armv7s
2013-07-23 17:03 802转自:http://witcheryne.iteye.co ... -
UITableView 修改编辑模式下的切图
2013-07-14 21:03 1168把以下代码放在点解 “编辑” 按钮时触发的方法中。 ... -
iOS平台UDID方案比较
2013-07-09 20:27 1525转自:http://www.cocoachina.com/b ... -
25 Free iOS Design Resources
2013-05-12 19:08 545Some great designers around th ... -
谈谈iOS Animation
2013-05-07 09:39 604转自:http://geeklu.com/2012/09/a ... -
IOS文件系统的管理
2013-05-06 17:11 628转自:http://blog.csdn.net/zhuzhi ... -
iOS应用的本地缓存机制设计
2013-05-06 16:18 677参考:http://www.cnblogs.com/peng ... -
iOS并发编程指南(3)
2013-04-28 14:37 750转自:http://mobile.51cto.com/iph ... -
iOS运行回路(RunLoop)总结
2013-04-27 23:24 677转自:http://www.cocoachina.com/i ...
相关推荐
n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---...
1. 类与对象:Objective-C中的所有操作都基于类和对象。为了实现汉诺塔,我们需要定义一个`Tower`类来表示柱子,它包含盘子数组和方法来添加、移除盘子。同时,还需要一个`Disk`类来表示盘子,包含盘子大小等属性。 ...
Objective-C 是苹果公司为其操作系统macOS和iOS开发的一种面向对象的编程语言,它极大地扩展了C语言,并引入了Smalltalk的动态特性。在本项目"Objective-C-HTML-Parser"中,我们关注的是如何在Objective-C中解析HTML...
在这份文档中,介绍了Objective-C的基本概念、类的设计与实现、对象的交互以及面向对象编程的高级特性。 Objective-C语言构建应用程序是基于对象网络的概念,这意味着程序是由相互连接的对象集合构成的。这些对象...
### Objective-C基础知识与核心概念 #### 一、Objective-C简介 Objective-C是一种面向对象的编程语言,它是在标准C的基础上发展起来的,主要用于Mac OS X和iOS等苹果平台的应用开发。Objective-C支持C语言的所有...
Objective-C实现全排列算法通常基于递归或回溯法。下面,我们将详细探讨如何使用Objective-C实现全排列算法,并通过数组保存结果。 首先,我们需要定义一个数组来存储原始数据,然后创建一个方法来处理全排列。这个...
n后问题--非递归迭代回溯.rar n后问题--非递归迭代回溯.rar n后问题--非递归迭代回溯.rar n后问题--非递归迭代回溯.rar n后问题--非递归迭代回溯.rar n后问题--非递归迭代回溯.rar
哈夫曼编码实现_c语言 (最小堆) 求WPL -----递归求解
20221011-1-递归与循环.py
Objective-C是苹果iOS和Mac OS X平台上主要的编程语言,它支持多种库来处理JSON数据。在iOS游戏或应用开发中,JSON由于其简洁、易于理解和解析的特性,成为与服务器进行HTTP网络通信时首选的数据格式。 在这个...
遍历递归的先中後序, 非递归的先中後序, 计算出深度 结点数 /* 运行结果: ------------------------ 请先序输入二叉树(如:ab三个空格表示a为根节点,b为左子树的二叉树) ab c 先序递归遍历二叉树: a b c 先序...
public static void TreeBuilder(List<T> all, T currentParentItem, long? parentId = null, string idProperty = "Id", string parentIdProperty = "ParentId", string childrenProperty = "ChildrenCollection...
[6.6.1]--413递归小结.srt
[6.6.1]--413递归小结.mp4
### Java程序设计基础:方法的递归调用 ...通过上述示例可以看出,递归调用的关键在于正确地定义递归基与递归体,并确保递归能够正确终止。此外,还需要注意递归调用的效率问题,避免不必要的重复计算。
[5.3.1]--403递归调用的实现.srt
[5.3.1]--403递归调用的实现.mp4
数据结构(c语言) 对于汉诺塔的递归实现。在对学习数据结构递归的人,帮助他们对汉诺塔和递归思想的理解
递归是指一个事物引用自身或与自身类似的实体。对于函数来说,递归意味着函数调用自身。对于对象,当对象通过指针或引用引用与其相似的事物时,就形成了递归。递归结构和递归函数经常相伴出现,许多对递归结构的常见...