Objective-C面向对象-用于处理分数的 Objective-C 类

来源:互联网 发布:新加坡dfs免税店 mac 编辑:程序博客网 时间:2024/06/11 16:38
谈过了对象,实例和方法这些基本的面向对象概念后。接下来不妨定义个实际的类,并学习如何使用类的实例。进而领略Objective-C摄人心魄的秩序和美丽。

范例3-1,下面是一个简单的处理分数的程序:

01 // Simple program to work with fractions
02 #import <Foundation/Foundation.h>
03 
04 int main (int argc, const char * argv[]) {
05     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
06     int numerator = 1;
07     int denominator = 3;
08     NSLog(@"The fraction is %i/%i", numerator, denominator);
09     
10     [pool drain];
11     return 0;
12 }

在这里,分数是以分子和分母的形式表示。创建自动释放池后,main中的前两行将变量numerator和denominator都声明为整型,并分别给它们赋值1和3,最终显示他们。似乎问题都解决了,不过试想如果程序现在需要处理的不是一个分数,而是多个。那么这种简单的过程性编码将遇到麻烦。每次要引用分数的时候,都必须引用相应的分子和分母,而且更进一步的处理也会更加复杂。

最终输出结果:

The fraction is 1/3



那么接下来,我们何不把一个分数定义成单个实体,用单个名称(例如myFraction)来共同引用它的分子和分母。
范例3-2,用一个名为Fraction的新来重写3-1中的函数:

01 // Program to work with fractions - class version
02 
03 #import <Foundation/Foundation.h>
04 
05 //---- @interface section ----
06 
07 @interface FractionNSObject
08 {
09     int numerator;
10     int denominator;
11 }
12 
13 -(void)        print;
14 -(void)        setNumerator: (int) n;
15 -(void)        setDenominator: (int) d;
16 
17 @end
18 
19 
20 //---- @implementation section ----
21 
22 @implementation Fraction
23 
24 -(void) print
25 {
26     NSLog (@"%i/%i", numerator, denominator);
27 }
28 
29 -(void) setNumerator: (int) n
30 {
31     numerator = n;
32 }
33 
34 -(void)setDenominator: (int)d
35 {
36     denominator = d;
37 }
38 @end
39 
40 
41 //---- program section ----
42 
43 int main (int argc, const char * argv[])
44 {
45     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
46     Fraction *myFraction;
47     
48     // Create an instance of a Fraction
49     
50     myFraction = [Fraction alloc];
51     myFraction = [myFraction init];
52     
53     // Set fraction to 1/3
54     
55     [myFraction setNumerator: 1];
56     [myFraction setDenominator: 3];
57     
58     // Display the fraction using the print method
59     
60     NSLog(@"The value of myFraction is:");
61     [myFraction print];
62     [myFraction release];
63     
64     [pool drain];
65     return 0;
66 }

从代码中我们可以看到,整个程序在逻辑上分为3个部分:
@interface部分
@implementation部分
program部分

@interface部分用于描述类,类的数据成分以及类的方法;
@implementation部分包括实现这些方法的实际代码;
program部分包含实现程序目的的程序代码。

下面具体介绍每个部分

@interface部分

定义新类时,必须做一些事情。
首先,要通知Objective-C编译器这个类来自何处。换句话说,我们必须命名它的父类。
其次,确定这个类对象要存储的数据类型。就是说,必须描述类成员将包含的数据。这些成员被称为实例变量
最后,定义在处理该类的对象时将要用到的各种操作或方法的类型

以上工作都在程序中名为@interface的特殊部分内完成。

一般的@interface格式:

@interface NewClassName: ParentClassName
{
memberDeclarations;
}

methodDeclarations;
@end

按照约定,类名以大写字母开头!这样做的好处是其他人在阅读你的程序时,仅仅通过观察第一个字母就能把类名和其他变量类型区分开来。那除了类名,在Objective-C中制定名称的基本规则是什么?答案是:名称必须以字母下划线(_)开头,之后可以是任何(大写或小写)字母,下滑线或者0到9之间的数字组合。不过存在少数的特例,就是被系统使用的保留字无法使用,比如int。

讲了这么多,回过头来看下本例中的情况。
newClassName 为 Fraction (新类)
ParentClassName 为 NSObject (父类)
memberDeclarations (数据类型) 不再复述。唯一需要注意的是每次创建新的对象时,将同时创建一组新的实例变量,而且是唯一一组。因此如果拥有两个Fraction,一个名为fracA,另一个名为fracB,那么每个都将有自己的一组实例变量。
methodDeclarations (方法类型) 
由于你不能直接访问分数的内部表示(就是,直接访问它的实例变量),因此你必须通过定义各种方法才能使用它。

-(void) print;

开头的负号(-)通知编译器,该方法是一个实例方法。如果是加号(+),则表示是类方法。类方法之前有提过就是对类本身执行某些操作的方法。例如创建类的新实例。好比制造厂制造一辆新的汽车。在这里汽车是个类;而要造新汽车便是类方法。

言归正传,实例方法对类的特定实例执行一些操作。例如设置值,检索值和显示值等。声明方法print时,必须通知Objective-C编译器这个方法是否返回值。void代表无返回值,如果是int表示返回整型值,double则返回双精度值,以此类推。当返回值定义了void后。就无需在方法结尾执行一条return语句了,因为return就是为了返回特定的值而存在。

再看@interface中还声明了其他两个方法:

-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;

它们都是不返回值的实例方法。每个方法都有一个整型参数,就是通过参数名前面的(int)指明的。就setNumerator来说,其参数名是n。这个名称可以是任意的,它是用来引用参数的方法名称。总结下声明这两个方法的语法。每个方法名都以冒号结束,这个冒号通知编译器该方法期望看到一个参数。然后,指定参数的类型,并将其放入一对圆括号中,这与方法指定返回值的类型非常相似。最后,使用象征性的名称来确定方法所指定的参数。整个声明以一个分号结束。

- 实例方法
(void) 返回值类型为无返回值
setNumerator 方法名
: 该方法期望看到一个参数
(int) 参数的类型为整型值
n 象征性的名称来确定方法所定的参数

@implementation部分

在@interface所声明的方法,在@implementation定义。所谓定义就是给出实际的代码。

一般的@interface格式:
@implementation NewClassName
methodDefinitions;
@end

NewClassName表示的名称与@interface部分的类名相同。可以在其后使用冒号加上父类名称。
例如:@implementation Fraction: NSObject
不过要声明的是它是可选的,而且通常并不这么做。

在代码中,print方法使用NSLog显示变量numerator和denominator的值。setNumerator和setDenominator将参数储存到各自的实例变量中去。

program部分

在这个部分中,包含了解决特定问题的代码

Fraction *myFraction;
定义了一个名为myFraction的对象。其中的星号(*)是必须的,它实际表示myFraction是对Fraction的一个引用(或者指针)。

myFraction = [Fraction alloc];
alloc 是 allocate 的缩写,因为要为新分数分配内存存储空间。表达式[Fraction alloc]向Fraction类发送一条消息。请求使用来自父类NSObject的alloc方法。使用方法的结果就是获得该类的新实例。把返回值储存在myFraction中。

myFraction = [myFraction init];
alloc方法保证对象的所有实例变量都变成初始状态。然而,这并不意味着该对象进行了适当的初始化进而可以使用。在分配对象后还必须进行初始化,init方法用于初始化类的实例变量,同样源自继承来的父类NSObject。现在,我们正将init消息发送给myFraction。就是说,要在这里初始化一个特殊的Fraction对象,因此它没有发送给类,而是发送给类的一个实例。把返回值储存在myFraction中。

由于这两条代码在Objective-C中非常常见。通常程序员都使用它们的组合形式:
myFraction = [[Fraction alloc] init];
和算数运算法则一样,内部的[Fraction alloc]首先求值,再应用init方法。初始化的过程一次性完成后把结果赋值给myFraction。

做为最终的简写形式,经常把分配和初始化直接合并到声明行,如下所示:
Fraction *myFraction = [[Fraction alloc] init];
而这种风格的延伸,将会在以后的代码中更经常被看到:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
此处将alloc消息发送给NSAutoreleasePool类,请求创建一个新实例。然后向新创建的对象发送init消息,以初始化该对象。

剩下的代码就非常简单了,不再多说。

代码最终输出结果很简单,不过我们已经在用面向对象的思想来处理却是个质的飞跃!

The value of myFraction is:

1/3


最后分享下笔者在CSDN上传的源代码链接:Simple_Fraction (范例3-1) Fraction_Class (范例3-2)