在面向对象编程中,类的行为一般称为方法,Objective-C中则称为消息,我们说的调用方法,在Objective-C中称为发送消息.和java一样,Objective-C 中有也有两种类型的方法:实例方法和类方法。

  • 实例方法,由类的实例来执行。换言之,在调用实例方法之前,必须先创建该类的实例。实例方法是最常见的方法类型。
  • 类方法,可由它所在的类直接执行。它不需要对象的实例作为消息的接收者。

方法声明

方法声明包含方法类型标识符、返回类型、一个或多个签名关键词,以及参数类型和名称信息。以下是方法声明的语法:

method
method

再来一个比较具体的例子:

method_decl_2x
method_decl_2x

对于实例方法,声明前面是减号 (-);对于类方法,对应指示器是加号 (+)。

一个方法的实际名称 (insertObject:atIndex:) 是所有签名关键词的串联,包括冒号字符。冒号字符表明有参数存在。在上述示例中,该方法采用两个参数。如果方法没有参数,则省略第一个(也是仅有的一个)签名关键词后面的冒号。

发送消息

当您想要调用一个方法时,通过给实施该方法的对象发送一则消息来实现。(虽然”发送消息”常用作”调用方法”,但实际上,Objective-C 在运行时才会执行实际地发送。)消息包含方法名称,以及方法所需的参数信息(类型要匹配)。您发送到一个对象的所有消息,都被动态地分派,这样使 Objective-C 类的多态行为更加容易。(多态性是指不同类型的对象响应同一消息的能力。)有时被调用的方法,是由接收消息对象的类之超类来实现。

要分派消息,运行时需要一个消息表达式。消息表达式使用方括号([ 和 ])将消息本身(以及任何所需参数)括起来,同时将接收消息的对象放在最左边方括号右侧。例如,要将 insertObject:atIndex:消息发送给 myArray 变量保存的对象,您会使用以下语法:

[myArray insertObject:anObject atIndex:0];

为避免声明大量局部变量来储存临时结果,Objective-C 让您嵌套消息表达式。每个嵌套表达式的返回值,都用作另一个消息的一个参数或接收对象。例如,可以将上个示例中使用的任何变量替换为取回值的消息。因此,如果您具有另一个名为 myAppObject 的对象,并且此对象具有访问数组对象的方法以及要插入数组的对象,则可以将上个示例编写为如下形式:

[[myAppObject theArray] insertObject:[myAppObject objectToInsert] atIndex:0];

Objective-C 还提供用于调用存取方法的点记法语法。存取方法获取并设定对象的状态,因此对于封装很重要,是所有对象的重要功能。对象隐藏或封装其状态,并显示接口,该接口是访问该状态的所有实例都通用的。使用点记法语法,您可以将上个示例重新编写为如下形式:

[myAppObject.theArray insertObject:myAppObject.objectToInsert atIndex:0];

您还可以使用点记法语法进行赋值:

myAppObject.theArray = aNewArray;

此语法只是编写 [myAppObject setTheArray:aNewArray]; 的另一种方式。在点记法表达式中,您不能使用对动态类型化的对象(类型为 id 的对象)的引用。

类方法

尽管前几个示例将消息发送到了类的实例,但您也可以将消息发送到类本身。(类是运行时创建的、类型为 Class 的对象。)向类发送消息时,您指定的方法必须定义为类方法,而非实例方法。类方法是一种功能,类似于 C++ 中的静态类方法。

您通常这样使用类方法:要么将类方法用作工厂方法创建类的新实例,要么访问与该类关联的一些共享信息。类方法声明的语法,与实例方法声明的语法相同,只是方法类型标识符使用加号 (+),而非减号。

块(Block)

块是封装工作单元的对象,即可在任何时间执行的代码段。块可以理解为java中的匿名函数,可作为方法和函数的参数传入,或可从方法和函数中返回。块本身具有一个已类型化的参数列表,且可能具有推断或声明的返回类型。您还可以将块赋值给变量,然后像调用函数一样调用它。

插入符号 (^) 是用作块的语法标记。块的参数、返回值和正文(即执行的代码)存在其他类似的语法约定。下图解释了该语法,尤其是在将块赋值给变量时。

blocks
blocks

您接着可以调用块变量,就像它是一个函数一样:

int result = myBlock(4); // result is 28

块共享局部词法作用范围内的数据。块的这项特征非常有用,因为如果您实现一个方法,并且该方法定义一个块,则该块可以访问该方法的局部变量和参数(包括堆栈变量),以及函数和全局变量(包括实例变量)。这种访问是只读的,但如果使用 __block 修饰符声明变量,则可在块内更改其值。即使包含有块的方法或函数已返回,并且其局部作用范围已销毁,但是只要存在对该块的引用,局部变量仍作为块对象的一部分继续存在。

作为方法或函数参数时,块可用作回调。被调用时,方法或函数执行部分工作,并在适当时刻,通过块回调正在调用的代码,以从中请求附加信息,或获取程序特定行为。块使调用方在调用时能够提供回调代码。块从相同的词法作用范围内采集数据(就像宿主方法或函数所做的那样),而非将所需数据打包在”关联”结构中。由于块代码无需在单独的方法或函数中实现,您的实施代码会更简单且更容易理解。

Objective-C消息机制

Objective-C和其他面向对象的语言不同,它强调消息传递,而非方法调用。因此你可以对一个对象传递任何消息,而不需要在编译期声名这些消息的处理方法。 很显然,既然编译期并不能确定方法的地址,那么运行期就需要自行定位了。而Objective-C runtime就是通过”id objc_msgSend(id theReceiver, SEL theSelector, …)“这个函数来调用方法的。其中theReceiver是调用对象,theSelector则是消息名,省略号就是C语言的不定参数了。 objc_msgSend方法含两个必要参数:receiver、方法名(即:selector), 如: [receiver message]; 将被转换为:objc_msgSend(receiver, selector); 带参数的方法则将转化成: objc_msgSend(receiver, selector, arg1, arg2, …);

消息是按名发送的,至于这个名字对应的是哪个方法,完全是运行时决定的。这体现出了Objective-C对象原生的多态性。消息的名字在Objective-C中叫做selector。在java和c++中,我们通常称之为方法的签名.

但selector中并不包含方法属主的信息,而且所有同名的方法都具有相同的selector。用C++的语言来解释就是,在不同类中的所有同名的方法都自动加入了同一个v-table中,无论它们之间是否有继承关系。这种按名调用的方式很接近于JavaScript等脚本语言,给程序开发提供了极大的灵活性。

SEL selector = @selector(message);

@selector是在编译期计算的,所以并不是函数调用。更进一步的测试表明,它在Mac OS X 10.6和iOS下都是一个C风格的字符串(char*): NSLog (@"%s", (char *)selector);

本文内容来源: