3.7 实例变量的访问以及数据封装
你已经看到处理分数的方法如何通过名称直接访问两个实例变量numerator和denominator的情况。事实上,实例方法总是可以直接访问它的实例变量。然而,类方法则不能,因为它只处理类本身,并不处理类的任何实例(仔细考虑一会儿)。但是,如果要从其他位置访问实例变量,例如,从main例程内部来访问,该如何实现?在这种情况下,不能直接访问这些实例变量,因为它们是隐藏的。将实例变量隐藏起来的这种做法实际上是称为数据封装的关键概念。它使得编写类定义的人在不必担心程序员(即类的使用者)是否破坏类的内部细节的情况下,扩展和修改他的类定义。数据封装提供了程序员和类的开发者之间的良好隔离层。
通过编写特殊方法来检索实例变量的值,可以用一种新的方式来访问它们。例如,创建两个非常恰当地命名为numerator和denominator的方法,它们用来访问作为消息接收者的Fraction的相应实例变量。结果将是相应整数值,你将返回这些值。以下是这两个新方法的声明:
-(int)numerator;
-(int)denominator;
下面是定义:
-(int)numerator
{
return numerator;
}
-(int)denominator
{
return denominator;
}
注意,它们访问的方法名和实例变量名是相同的。这样做不存在任何问题;事实上,这是很常见的情况。代码清单3-4用来测试这两个新方法。
代码清单3-4
//Program to access instance variables-contd
import<Foundation/Foundation.h>
//——@interface section——
@interface Fraction:NSObject
{
int numerator;
int denominator;
}
-(void)print;
-(void)setNumerator:(int)n;
-(void)setDenominator:(int)d;
-(int)numerator;
-(int)denominator;
@end
//——@implementation section——
@implementation Fraction
-(void)print
{
NSLog(@“i/%i”,numerator, denominator);
}
-(void)setNumerator:(int)n
{
numerator=n;
}
-(void)setDenominator:(int)d
{
denominator=d;
}
-(int)numerator
{
return numerator;
}
-(int)denominator
{
return denominator;
}
@end
//——program section——
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
Fraction*myFraction=[[Fraction alloc]init];
//Set fraction to 1/3
[myFraction setNumerator:1];
[myFraction setDenominator:3];
//Display the fraction using our two new methods
NSLog(@“The value of myFraction is:%i/%i”,
[myFraction numerator],[myFraction denominator]);
[myFraction release];
[pool drain];
return 0;
}
代码清单3-4输出
The value of myFraction is 1/3
NSlog语句
NSLog(@“The value of myFraction is:%i/%i”,
[myFraction numerator],[myFraction denominator]);
显示发送给myFraction:的两条消息的结果,第一条消息检索numerator的值,而第二条则检索denominator的值。
顺便说一下,设置实例变量值的方法通常总的称为设置函数(setter),而用于检索实例变量值的方法叫做获取函数(getter)。对于Fraction类而言,setNumerator:和setdenominator:是设置函数,numerator和denominator是获取函数。
注意不久就会看到Objective-C 2.0一个很方便的特性,即允许自动创建设置函数方法和获取函数方法。
这里还应指出,还有一个名为new的方法可以将alloc和init的操作结合起来。因此,程序行
Fraction*myFraction=[Fraction new];
可用于分配和初始化新的Fraction。
用两步来实现分配和初始化的方式通常更好,这样可以在概念上理解正在发生两个不同的事件:首先创建一个对象,然后对它初始化。