第10章 变量和数据类型

本章中,我们将讨论有关变量的作用域、对象的初始化方法以及数据类型的详细内容。

对象的初始化是本章中需要特别关注的内容。在第7章“类”中,已简要地介绍了实例变量、静态变量以及局部变量的作用域。这里将更深入地讲述静态变量,并且将引入全局变量和外部变量的概念。同时,还将给出一些指令,以便Objective-C编译器能够更准确地控制实例变量的作用域。本章还将讲述这些指令。

使用枚举数据类型,可以定义只存储一系列特定值的数据类型名称。Objective-C语言的typedef语句允许你对内置或派生的数据类型指派自己的名称。最后,在本章,我们将更详细地描述在表达式的求值过程中转换数据时,Objective-C编译器所遵循的确切步骤。

10.1 类的初始化

前面已经出现过这种模式:使用以下常见序列,分配对象的新实例,然后对它初始化:


Fraction*myFract=[[Fraction alloc]init];


调用这两个方法之后,通常向这个新对象指派一些值,如下所示:


[myFract setTo:1 over:3];


初始化对象之后为其设置初值的过程通常可合并到一个方法中。例如,你可以定义一个initWith:方法,它初始化一个分数,并将其分子和分母设置为两个给定的参数(没有给出名称)。

包含很多方法和实例变量的类通常还有几个初始化方法。例如,Foundation框架中的NSArray类包含了以下6个初始化方法:


initWithArray:

initWithArray:copyItems:

initWithContentsOfFile:

initWithContentsOfURL:

initWithObjects:

initWithObjects:count:


很可能会用下面的语句序列完成数组的空间分配和初始化工作:


myArray=[[NSArray alloc]initWithArray:myOtherArray];


常见的编程习惯是类中所有初始化方法都以init……开头。可以看到,NSArray的初始化方法遵循这个惯例。在编写初始化方法时,应该遵循以下两个策略。

如果你的类包含多个初始化方法,其中一个就应该是指定的(designated)初始化方法,并且其他所有初始化方法都应该使用这个方法。通常,它是最复杂的初始化方法(一般是参数最多的初始化方法)。通过创建指定的初始化方法,可以把大部分初始化代码集中到单个方法中。然后,任何人要想从该类派生子类,就可以重载这个指定的初始化方法,以便保证正确地初始化新的实例。

一定要恰当地初始化任何继承来的实例变量。最简单的方式就是首先调用父类指定的初始化方法,大多数情况下是init方法,然后,可以初始化自己的实例变量。

基于这个讨论,Fraction类的初始化方法initWith:可能如下所示:


-(Fraction*)initWith:(int)n:(int)d

{

self=[super init];

if(self)

[self setTo:n over:d];

return self;

}


这个方法首先调用父类的初始化方法,也就是NSObject的init方法(回忆一下,它是Fraction的父类)。初始化的结果需要指派回self,因为初始化方法有权更改或移动内存中的对象。

完成Super的初始化(返回的非零值表示初始化成功)之后,使用setTo:over:方法设置Fraction的分子和分母。和其他初始化方法一样,希望由你返回初始化的对象,在这里你就是这样做的。

代码清单10-1测试了新的初始化方法initWith:。

代码清单10-1


import“Fraction.h”

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

Fractiona,b;

a=[[Fraction alloc]initWith:1:3];

b=[[Fraction alloc]initWith:3:7];

[a print];

[b print];

[a release];

[b release];

[pool drain];

return 0;

}


代码清单10-1输出


1/3

3/7


开始执行程序时,它向所有类发送initialize调用方法。如果存在一个类及相关的子类,则父类首先得到这条消息。该消息只向每个类发送一次,并且向该类发送其他任何消息之前,保证向其发送初始化消息。这样做的目的是在程序开始时执行所有类的初始化工作。例如,你可能想在开始时初始化与类有关的静态变量。