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-2 特有的实例变量
[frac1 setNumerator:2];
中,只要setNumerator:在方法中使用名称numerator,引用的都是frac1的numerator。这是因为frac1是此消息的接收者。