从C的声明符到Objective-C的Blocks语法

Posted answerHuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从C的声明符到Objective-C的Blocks语法相关的知识,希望对你有一定的参考价值。

在这个 post 中,我先以 C 简单和内置复杂的声明开始,直到我们开始接触 Objective-C 的 Blocks 语法。当我接触 block 语法的时候也花了一段时间去理解,但是一旦你理解了 block 语法的结构和它的来源,那你下次用到 block 时,再也不要问 Google 大神啦。

如果你希望你写 block 的时候手到擒来,那就继续读下去吧!

申明

C 里面的变量都是用声明符声明的。

声明有两个规则:

指定变量的类型 (这是编译器希望在内存空间中找到的) 
给变量一个名字让他对分配的空间可用 (即给分配的空间取个名字)。
让我们定义一个最基础的声明:

int a;

这也许就是你曾经写过的C语言代码。

int 是基本类型,a 是变量名或叫做标识符。

当阅读一个声明的时候,你从标识符开始,一直向右看,然后重新从最左边的变量开始(我将在下一章解释为什么)。
这里我们定义的变量右边没有东西,所以这只是简单的说明了:a 是一个 int 类型的。

一个声明只能有一个基本类型,并且类型必须放在声明的最左边。

声明可以用修饰符修改基本类型来创建衍生类型。四个修饰符 (三个来自ANSI-C,一个来自 Apple's proposed extension) 分别为: *,[ ],( ) 和 ^ 。

三个 ANSI-C 修饰符

指针修饰符 *

int *a;
基本类型任然是 int,变量名为 a,但是指针修饰符 * 告诉我们 a 是一个指向 int 的指针而不是 int。

* 修饰符总是出现在被修饰变量的左边。

数组修饰符 [ ]

int a [ ];
这里数组修饰符 [ ] 告诉我们 a 现在是一个含有 int 的数组,而不是简单的 int。比如 int a[10];

[ ] 修饰符总是出现在被修饰变量的右边。

方法修饰符 ( )

int f();
方法修饰符 ( ) 告诉我们 f 是一个返回 int 类型的方法。这个修饰符也可以指定方法需要的参数,如:int f(long);是一个需要 long 类型作为变量,并且返回一个 int 类型的方法。

( )修饰符总是出现在被修饰变量的右边。

组合修饰符

指针和数组

修饰符可以被组合起在一起来创建更复杂的变量类型。跟算法优先级一样 ( * 和 / 在 + 和 - 之前执行),修饰符也是有优先级的。[ ] 和 ( ) 比 * 和 ^ 有更高的优先级。既然两个拥有更高优先级的修饰符写到了变量右边,当阅读复合声明时,你需要从标识符开始,向右阅读,一旦你阅读到声明结束或者右括号时,再向左阅读。

int *a [];
或者你也可以这样写增加阅读性:

int *(a[]);
这是一个保存了指向 int 类型指针的数组。

然后你可能想问,如果我想声明一个指向 int 类型数组的指针该怎么办?既然 * 比 [] 的优先级低,你需要使用圆括号来强制 * 修饰符有优先级。

int (*a)[];
这便是一个指向 int 类型数组的指针。

数组和方法

你不可能定义一个包含方法的数组,并且方法不能返回数组或方法。然而方法可以获得数组作为参数。

int f (int [10]);
这是一个方法,拥有一个含有 10 个 int 数组作为参数,并且返回一个 int 类型的值。

指针和方法


int *f();
int *(f());

在这两种情况一样, f 是一个方法,这个方法返回一个指向 int 类型的指针。

如果你想定义一个指向方法的指针该肿么办?圆括号!

int (*f)();
f是一个指针,指向一个返回 int 类型的方法。

block指针修饰符^

Apple 引进了第四个修饰符: ^ 。这个修饰符叫做 block 指针修饰符(或者像最初那样叫做闭包修饰符)。blocks 和指向方法的指针非常类似。你可以使用声明方法指针的方法来声明 block。

block 指针修饰符只能被应用于方法(你不可以写 int a;这是没有被定义的)。

这正是为什么 int b( ) 是非法的,并且会造成编译器错误:如果你使用优先级规则来读这个声明,b 将会是一个方法,这个方法返回一个指向 int 类型的 block 指针。你也知道,并没有这回事儿。所以当你声明一个 block 时,你总是需要将标识符和修饰符放在圆括号中了。

int (^b)();
b 是一个 block 指针,这个指针指向返回int类型的方法。

当然你也可以指定 block 所需要的参数:

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 使用在多个地方使用抽象声明符:当为方法声明参数或者返回值时。

- (long **) methodWithArgument:(int *)a;
这里 long ** 和 int * 都是抽象声明符。

所以为了在 Objective-C 方法中使用 blocks 作为参数或者返回值,我们需要寻找为那些 blocks 定义的抽象声明符。我们可以通过移去标识符。

int (^b)( ) 变成 int (^)( )int (^b)(long) 变成 int (^)(long).

例如:

- (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 ); 来说,它的本意可能会是:

block = ^(long a, long b)
{
int c = a + b;
return c;
}

结论

尽管看起来有点复杂,Objective-C 中的 blocks 语法是依赖于标准 C 语法的。Objective-C 中的 block 只不过是一个捕获函数范围的指针。一旦你理解这些概念,并且不断练习读写一些 blocks 声明,你将会发现 blocks 很好理解。


以上是关于从C的声明符到Objective-C的Blocks语法的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其它一些编程语言中的 lambdas 比較类似。

从C语言的变量声明到Objective-C中的Block语法

Windows下配置Objective-C开发环境

如何在 Objective-C 中编写 lambda 方法?

[OC学习笔记]Blocks

从objective-c++调用swift函数