3.6 Program部分

program部分包含解决特定问题的代码,如果有必要,它可以跨越多个文件。前面提到,必须在其中一个地方有一个名为main的例程。通常情况下,这是程序开始执行的地方。以下是代码清单3-2的program部分:


//——program section——

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

{

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

Fraction*myFraction;

//Create an instance of a Fraction

myFraction=[Fraction alloc];

myFraction=[myFraction init];

//Set fraction to 1/3

[myFraction setNumerator:1];

[myFraction setDenominator:3];

//Display the fraction using the print method

NSLog(@“The value of myFraction is:);

[myFraction print];

[myFraction release];

[pool drain];

return 0;

}


在main中,使用以下程序行定义了一个名为myFraction的变量:


Fraction*myFraction;


这个行表示myFraction是一个Fraction类型的对象;就是说,myFraction用于存储来自新的Fraction类变量。myFraction前面的星号(*)是必需的,但现在不用担心它的意义。在技术上,它实际表示myFraction是对Fraction的一个引用(或者指针)。

现在,你拥有一个用于存储Fraction的对象,需要创建一个分数,就像要求制造厂制造一辆汽车一样。可以用以下程序行实现:


myFraction=[Fraction alloc];


alloc是allocate的缩写。因为要为新分数分配内存存储空间。表达式


[Fraction alloc]


向新创建的Fraction类发送一条消息。你请求Fraction类使用alloc方法,但是你从未定义这个alloc方法,那么它来自何处呢?此方法继承自一个父类。第8章将会详细讨论这个主题。

将alloc消息发送给一个类时,便获得该类的新实例。在代码清单3-2中,返回值存储在变量myFraction中。alloc方法保证对象的所有实例变量都变成初始状态。然而,这并不意味着该对象进行了适当的初始化进而可以使用。在分配对象之后,还必须对它初始化。

这项工作可用代码清单3-2中的下一条语句来完成。如下:


myFraction=[myFraction init];


这里再次使用了一个并非自己编写的方法。init方法用于初始化类的实例变量。注意,你正将init消息发送给myFraction。就是说,要在这里初始化一个特殊的Fraction对象,因此它没有发送给类,而是发送给了类的一个实例。继续下面内容之前,务必理解这一点。

init方法也可以返回一个值,即被初始化的对象。将返回值存储到Fraction的变量myFraction中。

下面两行代码分配了类的新实例并做了初始化,这两条消息在Objective-C中特别常见,通常组合到一起,如下所示:


myFraction=[[Fraction alloc]init];


内部消息表达式


[Fraction alloc]


将首先求值。可以看到,这条消息表达式的结果是已分配的实际Fraction。对它直接应用init方法,而不是像以前那样把分配结果存储到一个变量中。因此,再次先分配一个新的Fraction,然后对它初始化,最后把初始化的结果赋值给变量myFraction。

作为最终的简写形式,经常把分配和初始化直接合并到声明行,如下所示:


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


这种编码的风格将在本书的其余部分广泛使用,因此理解这种风格非常重要。到目前为止,在每个程序中都看到了如何分配自动释放池:


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


此处将alloc消息发送给NSAutoreleasePool类,请求创建一个新实例。然后向新创建的对象发送init消息,以初始化该对象。

回到代码清单3-2,现在已经能够设置分数的值。程序行


//Set fraction to 1/3

[myFraction setNumerator:1];

[myFraction setDenominator:3];


用于完成这项工作。第一条消息语句把消息setNumerator:发送给myFraction。提供一个值为1的参数。然后将控制发送给为Fraction类定义的setNumerator:方法。因为Objective-C系统知道myFraction是Fraction类的对象,因此它知道setNumerator:是要从Fraction类使用的一个方法。

在setNumerator:方法中,将传递来的值1存储在变量n中。该方法中的唯一程序行获得该值并由实例变量numerator存储这个值。因此,myFraction的分子已经被有效地设置为1。

其后的消息用于调用myFraction的setDenominator:方法。在setDenominator:方法中,参数3被赋值给变量d。然后把这个值存储到实例变量denominator中,这样就将myFraction赋值为1/3。现在可以显示此分数的值,用代码清单3-2的如下代码行来实现:


//display the fraction using the print method

NSLog(@“The value of myFraction is:);

[myFraction print];


NSLog调用仅显示以下文本:


The value of myFraction is:


使用以下消息表达式调用print方法:


[myFraction print];


在print方法中,将显示实例变量numerator和denominator的值,并用斜杠字符(/)将其分隔。程序中的消息


[myFraction release];


用于释放Fraction对象使用的内存。执行这个操作是良好的程序设计风格的重要部分。只要创建一个新对象,都要请求为该对象分配内存。同样,在完成对该对象的操作时,必须释放它所使用的内存空间。尽管“程序以任何方式终止时,都将释放内存”是事实,但开始创建更复杂的应用程序之后,最终可以生成成百(或上千)的对象,它们占用大量内存。等到程序终止时才释放内存是对内存的浪费,这会减慢程序的执行速度,这种方式并非好的程序设计风格。因此,要从现在开始养成良好的习惯!

Apple的运行时系统提供了一个称为垃圾回收的机制,它可自动清理内存。但是,最好要学会如何自己管理内存的使用,而不是依赖于自动的机制。事实上,针对不支持垃圾回收的某些平台(如iPhone)进行程序设计时,就不能依赖于垃圾回收机制。所以,我们在本书的后面才会介绍这个机制。

似乎要在代码清单3-2中编写大量的代码才能完成代码清单3-1所实现的工作。对于此处这个简单的例子来说确实如此;然而,使用对象的最终目的是使程序易于编写、维护和扩展。以后你将认识到这一点。

本章最后一个例子展示如何在程序中使用多个分数。在代码清单3-3中,将一个分数设置为2/3,另一个设置为3/7,然后同时显示它们。

代码清单3-3


//Program to work with fractions-contd

import<Foundation/Foundation.h>

//——@interface section——

@interface Fraction:NSObject

{

int numerator;

int denominator;

}

-(void)print;

-(void)setNumerator:(int)n;

-(void)setDenominator:(int)d;

@end

//——@implementation section——

@implementation Fraction

-(void)print

{

NSLog(@“i/%i”,numerator, denominator);

}

-(void)setNumerator:(int)n

{

numerator=n;

}

-(void)setDenominator:(int)d

{

denominator=d;

}

@end

//——program section——

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

{

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

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

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

//Set 1st fraction to 2/3

[frac1 setNumerator:2];

[frac1 setDenominator:3];

//Set 2nd fraction to 3/7

[frac2 setNumerator:3];

[frac2 setDenominator:7];

//Display the fractions

NSLog(@“First fraction is:);

[frac1 print];

NSLog(@“Second fraction is:);

[frac2 print];

[frac1 release];

[frac2 release];

[pool drain];

return 0;

}


代码清单3-3输出


First fraction is:

2/3

Second fraction is:

3/7


@interface和@implementation部分和代码清单3-2一样。这个程序创建了两个名为frac1和frac2的Fraction对象,然后将它们分别赋值为2/3和3/7。注意下面这一点,当frac1使用setNumerator:方法将其分子设置为2时,实例变量frac1也将实例变量numerator设置为2。同样,frac2使用相同的方法将其分子设置为3时,它特有的实例变量numerator也被设置为3。每次创建新类时,该类就获得了自己特有的一组实例变量。图3-2描述了这个情况。

根据要发送消息的对象,引用正确的实例变量。因此,在

3.6 Program部分 - 图1

图 3-2 特有的实例变量


[frac1 setNumerator:2];


中,只要setNumerator:在方法中使用名称numerator,引用的都是frac1的numerator。这是因为frac1是此消息的接收者。