从C的声明符到Objective-C的Blocks语法
原文链接:http://nilsou.com/blog/2013/08/21/objective-c-blocks-syntax/
在这个post中,我先以C简单和内置复杂的声明开始,直到我们开始接触Objective-C的Blocks语法。当我接触block语法的时候也花了一段时间去理解,但是一旦你理解了block语法的结构和它的来源,那你下次用到block时,再也不要问Google大神啦。
如果你希望你写block的时候手到擒来,那就继续读下去吧!
申明
C里面的变量都是用声明符声明的。
声明有两个规则:
- 指定变量的类型(这是编译器希望在内存空间中找到的)
- 给变量一个名字让他对分配的空间可用(即给分配的空间取个名字)。
让我们定义一个最基础的声明:
|
1
|
int a; |
这也许就是你曾经写过的C语言代码。
int是基本类型,a是变量名或叫做标识符。
当阅读一个声明的时候,你从标识符开始,一直向右看,然后重新从最左边的变量开始(我将在下一章解释为什么)。
这里我们定义的变量右边没有东西,所以这只是简单的说明了:a是一个int类型的。
一个声明只能有一个基本类型,并且类型必须放在声明的最左边。
声明可以用修饰符修改基本类型来创建衍生类型。四个修饰符(三个来自ANSI-C,一个来自Apple’s proposed extension)分别为:*,[ ],( )和^。
三个ANSI-C修饰符
指针修饰符 *
|
1
|
int *a; |
基本类型任然是int,变量名为a,但是指针修饰符*告诉我们a是一个指向int的指针而不是int。
* 修饰符总是出现在被修饰变量的左边。
数组修饰符 [ ]
|
1
|
int a [ ]; |
这里数组修饰符[ ]告诉我们 a 现在是一个含有int的数组,而不是简单的int。比如int a[10];
[ ]修饰符总是出现在被修饰变量的右边。
方法修饰符 ( )
|
1
|
int f(); |
方法修饰符 ( )告诉我们f是一个返回int类型的方法。这个修饰符也可以指定方法需要的参数,如:int f(long);是一个需要long类型作为变量,并且返回一个int类型的方法。
( )修饰符总是出现在被修饰变量的右边。
组合修饰符
指针和数组
修饰符可以被组合起在一起来创建更复杂的变量类型。跟算法优先级一样(* 和/在+和-之前执行),修饰符也是有优先级的。[ ]和( )比*和^有更高的优先级。既然两个拥有更高优先级的修饰符写到了变量右边,当阅读复合声明时,你需要从标识符开始,向右阅读,一旦你阅读到声明结束或者右括号时,再向左阅读。
|
1
|
int *a []; |
或者你也可以这样写增加阅读性:
|
1
|
int *(a[]); |
这是一个保存了指向int类型指针的数组。
然后你可能想问,如果我想声明一个指向int类型数组的指针该怎么办?既然*比[]的优先级低,你需要使用圆括号来强制*修饰符有优先级。
|
1
|
int (*a)[]; |
这便是一个指向int类型数组的指针。
数组和方法
你不可能定义一个包含方法的数组,并且方法不能返回数组或方法。然而方法可以获得数组作为参数。
|
1
|
int f (int [10]); |
这是一个方法,拥有一个含有10个int数组作为参数,并且返回一个int类型的值。
指针和方法
|
1
2
|
int *f();int *(f()); |
在这两种情况一样,f是一个方法,这个方法返回一个指向int类型的指针。
如果你想定义一个指向方法的指针该肿么办?圆括号!
|
1
|
int (*f)(); |
f是一个指针,指向一个返回int类型的方法。
block指针修饰符^
Apple引进了第四个修饰符:^。这个修饰符叫做block指针修饰符(或者像最初那样叫做闭包修饰符)。blocks和指向方法的指针非常类似。你可以使用声明方法指针的方法来声明block。
block指针修饰符只能被应用于方法(你不可以写int ^a;这是没有被定义的)。
这正是为什么int ^b( )是非法的,并且会造成编译器错误:如果你使用优先级规则来读这个声明,b将会是一个方法,这个方法返回一个指向int类型的block指针。你也知道,并没有这回事儿。所以当你声明一个block时,你总是需要将标识符和修饰符放在圆括号中了。
|
1
|
int (^b)(); |
b是一个block指针,这个指针指向返回int类型的方法。
当然你也可以指定block所需要的参数:
|
1
|
int (^b)(long); |
是一个block,需要一个long类型作为参数,并且返回int。
现在,你应该已经知道一些你需要记住的block语句的语法了:第一,是定义block的名字,另一个是要把block传递给Objective-C方法。
抽象声明符
声明符由两部分组成:一个你插入到标识符的抽象声明符。
抽象声明符在标准C中被用在三种情况:
1.在 int *a; long *b = (long *) a; 中, (long *) 是一个指向long的抽象声明符
2.作为sizeof()的参数:malloc(sizeof(long *));
3.为方法声明参数类型时:int f(long *);
Objective-C使用在多个地方使用抽象声明符:当为方法声明参数或者返回值时。
|
1
|
- (long **) methodWithArgument:(int *)a; |
这里long ** 和int *都是抽象声明符。
所以为了在Objective-C方法中使用blocks作为参数或者返回值,我们需要寻找为那些blocks定义的抽象声明符。我们可以通过移去标识符。
int (^b)( )变成int (^)( ), int (^b)(long)变成 int (^)(long).
例如:
|
1
2
|
- (void) methodWithArgument: (int(^)( )) block;- (void) anotherMethodWithArgument: (void(^)(long arg1)) block; |
然而在这些抽象声明中你不需要为你的block参数取名,这是一个很好的主意。当block期望作为参数时,这将是一个很好的暗示,并且当使用这种方法时Xcode是完全自动的。
Block 字面意思
当你写 int a = 2;,int a是一个声明,2是int的字面意思。
^也被用作为一元运算符来改造一个方法实现为block。你不需要指定block返回类型。
既然这是block的实现,你需要在这里面定义你的参数。
对于block int (^block)(long, long);来说,它的字面意思可能会是:
|
1
2
3
4
5
|
block = ^(long a, long b){ int c = a + b; return c;} |
结论
尽管看起来有点复杂,Objective-C中的blocks语法是依赖于标准C语法的。Objective-C中的block只不过是一个捕获函数范围的指针。一旦你理解这些概念,并且不断练习读写一些blocks声明,你将会发现blocks很好理解。
从C的声明符到Objective-C的Blocks语法的更多相关文章
- ISO/IEC 9899:2011 条款6.7.6——声明符
6.7.6 声明符 语法 1.declarator: pointeropt direct-declarator direct-declarator: identifier ( declar ...
- Javascript 使用 async 声明符和 await 操作符进行异步操作
async function 声明用于定义一个返回 AsyncFunction 对象的异步函数 await 操作符用于等待一个Promise 对象.它只能在异步函数 async function 中 ...
- Objective-C( 语法二)
分类(Category):可以给某一个类扩充一些方法(不修改原来类的代码) 作用:在不改变原来类内容基础上,可以为类增加一些方法 使用注意: 1. 只能增加方法,不能增加成员变量 2. 分类方法 ...
- C++ Primer 第二章 引用 指针 const限定符
1.引用: 为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d也就是声明的变量名(声明符就是变量名). PS:1.通过图片中编译所提示的报错信息 ...
- [C/C++]C++声明
[注]本文是Declarations的翻译和注解版. https://msdn.microsoft.com/en-us/library/f432x8c6.aspx 1.声明: 我们通过声明往C++程序 ...
- 详解C/C++函数指针声明
要理解一个C程序,仅仅理解组成该程序的符号是不够的.程序员还必须理解这些符号是如何组合成声明.表达式.语句和程序的. 我们先来看看下面的一个语句: 1 ( *( void(*)())0)(); 这是当 ...
- 关于C/C++函数指针声明的理解
[前言] 由于最近对函数指针的理解比较模糊,所有又重新学习了一把关于函数指针的知识,参考了很多书籍和网上的文章.现在本人进行一下分享和总结.本文的其实只是整理和总结别人现有的文章,作为备用参考文档. ...
- c语言复杂声明解析
这是个好东西,接触c语言好几年了,第一次看到这东西,惊喜万分. 先提供个分析案例,以后看方便 vector <int> * (*seq_array[]) (int )={func1,fun ...
- USB HID设备报告描述符详解(转)
转自:http://group.ednchina.com/93/198.aspx. 参考:USB HID usage table 概述: 报告在这里意思是数据传输(data transfer),而 ...
随机推荐
- 11、JQuery知识点总结
1.JQuery简介 JQuery 是一套跨浏览器的JavaScript库,简化HTML与JavaScript之间的操作 jQuery有下列特色: 跨浏览器的DOM元素选择 DOM巡访与更改:支持CS ...
- Java 语言概述与开发环境(1)
目录: 一.计算机语言的发展史 二.Java语言的简述 三.Java的特点 四.java语言的运行环境及环境变量的配置 五.Dos的常见命令 六.第一个java程序-HelloWord ...
- Leetcode 564.寻找最近的回文数
寻找最近的回文数 给定一个整数 n ,你需要找到与它最近的回文数(不包括自身). "最近的"定义为两个整数差的绝对值最小. 示例 1: 输入: "123" 输出 ...
- 将数据缓存到sessionStorage中
//获取侧边栏 if (sessionStorage.getItem(`${env}${empId}leftMenu`)) { const leftMenu = JSON.parse(sessionS ...
- POJ 3321:Apple Tree(dfs序+树状数组)
题目大意:对树进行m次操作,有两类操作,一种是改变一个点的权值(将0变为1,1变为0),另一种为查询以x为根节点的子树点权值之和,开始时所有点权值为1. 分析: 对树进行dfs,将树变为序列,记录每个 ...
- HDU 5289 Assignment(二分+RMQ-ST)
Assignment Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total ...
- Java接口对Hadoop集群的操作
Java接口对Hadoop集群的操作 首先要有一个配置好的Hadoop集群 这里是我在SSM框架搭建的项目的测试类中实现的 一.windows下配置环境变量 下载文件并解压到C盘或者其他目录. 链接: ...
- 洛谷P1822 魔法指纹 【分块打表】
题目 对于任意一个至少两位的正整数n,按如下方式定义magic(n):将n按十进制顺序写下来,依次对相邻两个数写下差的绝对值.这样,得到了一个新数,去掉前导0,则定义为magic(n).若n为一位数, ...
- 企鹅的游戏(penguin)
企鹅的游戏(penguin) 题目描述 Shiva养了一只小企鹅.小企鹅很聪明,她总是帮Shiva和他的好朋友想出很多很好玩的游 戏.其中有一个游戏特别经典,Shiva和他的小伙伴们百玩不厌. 游戏规 ...
- zookeeper与Kafka集群搭建及python代码测试
Kafka初识 1.Kafka使用背景 在我们大量使用分布式数据库.分布式计算集群的时候,是否会遇到这样的一些问题: 我们想分析下用户行为(pageviews),以便我们设计出更好的广告位 我想对用户 ...