Objective-C语言学习基础是本文要介绍的内容,在面向对象编程中必须具有的最基本的功能是向对象发送消息。在Objective-C中,我们采用以下的结构发送消息:
- [receiver message];
receiver是你要向它发送消息的对象,message是你要接收对象执行的method。例如,假设在我们的程序中有一个对象dataTable,我们想要更新它的数据。要这样做,我们要在程序中包含以下的语句,给数据表发送消息:
- [dataTable reloadData];
在Objective-C的method中,参数用冒号进行标记。
- [textField setEditable:YES];
你可以有多个参数,每个以冒号为引导。消息也可以进行嵌套,如果参数类型和返回类型是一致的话。例如,我们可以读取滚动条的值并把它在文本区内显示出来。
- [textField setDoubleValue:[slider doubleValue]];
这里,[slider doubleValue]是[textField setDoubleValue: ]的参数。doubleValue命令返回值为double类型,与setDoubleValue的输入参数类型一致。
关于数据类型,在Objective-C中,对象的类型全部为id。id类型的变量仅仅只是对象的描述符而已。事实上,它们是指向对象数据结构的指针,这个超出了我们讨论的范围。在代码中,我们象使用其它变量一样,产生指向对象的变量。
- id anObject;
在Objective-C中,缺省的返回数据类型为id。所以,如果你的method没有指定返回类型,它会自动分配为id。
也可以把一个数据变量的类型指定为特定的类。这称作静态类型。所有指向对象的变量实际上都是指向它们内存位置的指针。这种实现方式在多数情况下,对编程人员是透明的。当然,如果是静态类型的话,就不是透明的了。
当我们创建一个id类型的变量,事实上它隐含为一个对象的指针。id定义为指针类型——一个对象的标识。但如果你有一个指向字符串的变量,而且你象把它静态声明为NSString,你必须在代码中显式地声明这个变量是一个指针。这可以通过C的指针声明来实现:
- NSString *aString;
变量名前面的星号不是变量名的一部分,而是表明aString是NSString这个对象的指针。这是你唯一需要知道的关于对象变量的指针属性的知识。只有在声明新的变量的时候才需要使用C的指针结构。在你随后引用aString的时候,它就好象其它变量一样。
如果你对指针或C语言本身不熟悉,可以参考Brian Kerrnighan和Dennis Ritchie(C语言的作者)编写的《C编程语言》。另外,我还想推荐Steve Oulline编写的《使用C编程》。
其它产生嵌套method的方法
现在我们有一个对象的数据类型,我们可以用与上面不同的方式产生嵌套的method。一个返回id类型的消息可以作为其它消息的接收者。
- double number;
- number=[anArray lastObject] doubleValue];
当然,我们假定[anArray lastObject]返回的对象能够相应doubleValue这个消息。我们拥有了一个对象的阵列。[anArray lastObject]返回的对象处于阵列的最顶端,然后它接收到一个doubleValue的消息。
在Objective-C中定义新的类
现在,我想和你讨论在Objective-C源代码中如何定义一个新的类。按照界面和实现分开的精神,Objective-C在两个分开的文件中定义新的类——一个界面文件和一个实现文件。
界面文件包括使用类所需要的全部信息,包括所有实例变量的声明和method的定义。程序员通过研究界面文件了解哪一个类适合使用。实现文件包括method如何实现的源代码。
一个类的界面和实现往往分在两个文件中,虽然对编译器来说并没有这样的强制要求。实现文件的后缀为:.m,也就是Objective-C源文件的扩展名。界面文件的扩展名为.h,和C语言的头文件是一样的。类文件的名字通常与类的名字一样,虽然这也不是编译器的强制要求。这样,一个叫“Circle”的类会在文件Circle.h和Circle.m中定义。
界面文件
界面文件声明构成类的实例变量和method。一个界面文件的结构为:
- @interface 类的名字 : 它的超类的名字
- {
- 实例变量的声明
- }
- method的声明
- @end
界面声明总以编译器指令@interface开始,@end结束。在***行跟在@interface之后的是类的名字。在类的名字后面是一个冒号,这之后是你的类所继承的类的名字——它的超类。如果你没有指定超类,那么它就会假定你是在创建一个新的根类,就好象Cocoa中的NSObject一样。
***行以后在大括号内的是实例变量的声明。这是method所要操作的类的数据结构。在我们假设的Circle类中,实例变量的声明也许会是这样的:
- double radius;
- double xLacation;
- double yLocation;
- NSColor *color;
实例变量的变量声明总是以数据类型在前面。后面我们会学到,NSColor是操作颜色的应用库的类。
在实例变量声明之后是method的声明。象标准的C函数,Objective-C中的method有返回值和输入参数。另外,method既可以是类的也可以是实例的。类method只能由前面讨论过的类对象来调用,而实例method则可以由类的任何对象来调用。method名前面的加号表示这是一个类method。
- + alloc
以减号开头的method声明表示这是一个实例method。
- - (void) setXLocation: (double)x YLocation: (double)y;
method的返回值由method名前面括号内的类型确定。参数跟在冒号之后,多个参数由参数名和空格分开。象method的返回类型一样,参数的数据类型也是由参数名前面括号内的类型决定。
如果一个类与它的超类由关联,应该导入它的超类的界面文件,通常是超类的名字后面加上.h构成。
- #import "ItsSuperclass.h"
#import语句的作用和你也许已经很熟悉的#include很相似——区别只是#import更智能化一些。#import***的优势在于它自动检查一个文件是否已经被导入过,而不会重复进行导入。#include并不进行这个检查。
实现文件
实现文件是你的类的代码核心部分。这个文件包括你使得你的method运作的所有代码——使得你的类能够完成一些有意义的工作。界面文件使你的类的描述性部分,使其它程序员编写的代码可以与你的类一起工作。实现文件使你的类完成需要的工作。
实现文件的格式与界面文件的有些类似:
- #import "类名.h"
- @implementation 类名 : 超类名
- {
- 实例变量声明
- }
- method定义
- @end
每个类的实现都必须引用它的界面文件。因为界面文件中已经声明了你所继承的超类,因此这部分可以省略。这更加明确了实现文件主要关于method实现的概念。这样,在实践中,实现文件只需要以下的代码:
- #import "类名.h"
- @implementation 类名
- method定义
- @end
method的定义和C函数的定义类似。名字应该与界面文件完全一致(没有分号),method的实现代码包括在名字后面的一对大括号中。例如,我们的Circle类的method为:
- + alloc
- {
- 你的代码
- }
- - (void)setXLocation: (double)x YLocation: (double)y
- {
- 你的代码
- }
你也许会奇怪为什么+ alloc没有返回值。Objective-C的缺省返回值是id——也就是说,它缺省返回一个对象。+ alloc这个method设计为返回类的一个对象,因此没有必要指明返回值。在提到+ alloc这个method时,我要指出你很少需要自己实现这个method。NSObject这个类会处理这个问题。+ alloc的目的是为新创建的对象分配内存。下面的内容会涉及创建新对象的细节。
实例变量可以在你的method中直接引用。不需要特别指明,所有实例变量与method处于同一个名域。它们就象C中全局变量。这样,我们可以象下面那样定义第二个method:
- - (void)setXLocation: (double)x YLocation: (double)y
- {
- xxLocation = x;
- yyLocation = y;
- }
另外,也可以定义局部变量,它们具有更加局限的作用区域。例如,在上面的第二个method中,我们可以添加一个没用的中间变量:
- - (void)setXLocation: (double)x YLocation: (double)y
- {
- double tempX;
- double tempY;
- tempX = x;
- tempY = y;
- xLocation = tempX;
- yLocation = tempY;
- }
你会发现任何可以应用于标准C函数的,都可以应用于类的method。
创建新对象
要创建一个新对象,你可以向你想创建的实例所属的类的类对象发送一个alloc消息。例如,我想创建Circle类的一个实例,我们可以这样做:
- id aCircle;
- aCircle = [Circle alloc];
记住,alloc返回一个对象,所以你打算存储你所创建的实例的变量应该是id类型的。一旦创建了新的实例,我们需要初始化它的实例变量。这可以想新创建的对象发送一个init消息。
- [aCircle init];
初始化必须在分配内存之后立刻进行,所以一般用两个嵌套的消息来完成。
- aCircle=[[Circle alloc] init];
缺省地,init把所有实例变量初始化为0。你可以创建你自己的初始化method,也称为构造函数,来安装你的需求初始化变量。通常构造函数以“init”开头。因为构造函数需要和一个对象的实例变量打交道,所以它应该是一个实例method,而不是象“alloc”那样的类method。例如,你可以为我们的Circle类创建一个构造函数,把半径初始化为10。这个method应该是这样的:
- - (void)initWithRadius: (double)r;
- {
- rradius = r;
- }
任何没有在你的构造函数中初始化的实例变量缺省地设置为0。
在Project Builder中
到现在位置,我还没有提到Project Builder。但我现在想谈到一个与现在讨论有关的特性。Project Builder包含类的界面和实现文件的模板。你获得是一个继承自基础库中NSObject类的框架——你所需要做的是用你自己的数据结构和method来填充它。所以,现在你应该了解类在源代码级的结构,同时也知道怎么简单地创建它。
现在我们已经基本介绍完入门的知识,下面将要进入真实程序的编程。下一次,我将会介绍Interface Builder。我们对Interface Builder的学习主要是通过实验。
小结:初学者开发文档:Objective-C语言学习基础的内容介绍完了,希望本文对你有所帮助!