10.2.3 静态变量
前面所示的例子与数据封装原则以及良好的面向对象编程技术相违背。然而,可能需要下面这种变量:它们的值在经过不同的方法调用时是共享的。虽然在Foo类中将gGlobalVar定义为实例变量似乎也不太合理,但是,更好的方法可能是通过将访问限制在类中定义的setter和getter方法中,将实例变量“隐藏”在Foo类中。
现在,你知道在方法之外定义的变量不仅是全局变量而且是外部变量。然而,在很多情况下你想要将变量定义为全局变量但不是外部变量。换句话说,希望定义的全局变量只在特定模块(文件)中是全局的。这种变量在下面的情况中很有意义:除了特定类中的方法,再没有其他方法需要访问这个特定变量。要做到这一点,可以在包含这个特定类实现的文件中将该变量定义为static。
如果语句
static int gGlobalVar=0;
声明在任何方法(或函数)之外,那么在该文件中,所有位于这条语句之后的方法或函数都可以访问gGlobalVar的值,而其他文件中的方法和函数则不行。
你会想起类方法不能访问实例变量(你可能考虑为什么又是这样)。然而,可能希望类的方法可以设定和访问一些变量。简单的例子是类的分配器方法,它要记录类已经分配空间的对象数目。实现这项任务的方式是在类的实现代码文件中设定静态变量。由于这个变量不是实例变量,所以分配器方法可以直接访问它。类的用户不用知道这个变量。因为它是定义在实现文件中的静态变量,作用域只是文件内部。因此,用户不能直接访问该变量,也就没有违背数据封装的概念。如果需要从类之外访问该变量,则可以编写一个方法来获取该变量的值。
代码清单10-2对Fraction的类定义进行了扩充,增加了两个新方法。allocF类方法分配一个新的Fraction对象,同时记录分配了多少Fraction, count方法则返回这个数的值。注意,后者也是类方法。也可以作为实例方法实现,然而,与向类的特定实例发送消息相比,询问类已经分配了多少实例更有意义。
下面是在头文件Fraction.h中添加的这两个新的类方法声明:
+(Fraction*)allocF;
+(int)count;
你可能注意到,继承来的alloc方法并没有被重载;相反,你定义了自己的分配器方法。这个方法就可以利用继承来的alloc方法。下面是需要在实现文件Fraction.m中加入的代码:
static int gCounter;
@implementation Fraction;
+(Fraction*)allocF
{
extern int gCounter;
++gCounter;
return[Fraction alloc];
}
+(int)count
{
extern int gCounter;
return gCounter;
}
//other methods from Fraction class go here
……
@end
注意重载alloc并不是好的编程实践,因为这个方法处理内存的物理分配。你不应该涉足那个领域。
count声明为静态使得定义在执行文件中的方法可以访问它,但是该文件之外都不可以访问。allocF方法仅仅递增gCounter变量,然后使用alloc方法创建一个新的Fraction,并返回结果;gCounter方法只是返回计数器的值,这样就隔离了来自用户的直接访问。
回忆一下,因为gCounter变量定义在该文件中,因此不需要在这两个方法中使用extern声明。声明只是为了让阅读该方法的人明白:他访问的变量是定义在该方法之外。变量名加g前缀也是出于同样的目的;因此,大多数程序员一般不使用extern声明。
代码清单10-2测试这个新方法。
代码清单10-2
import“Fraction.h”
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
Fractiona,b,*c;
NSLog(@“Fractions allocated:%i”,[Fraction count]);
a=[[Fraction allocF]init];
b=[[Fraction allocF]init];
c=[[Fraction allocF]init];
NSLog(@“Fractions allocated:%i”,[Fraction count]);
[a release];
[b release];
[c release];
[pool drain];
return 0;
}
代码清单10-2输出
Fractions allocated:0
Fractions allocated:3
程序开始运行时,gCounter的值会自动置为0(你应该还记得,如果要将类作为整体进行任何特殊初始化,例如,将其他静态变量的值设置为一些非零值,可以重载继承类的initialize方法)。使用allocF方法分配三个Fraction实例之后,count方法检索counter变量的值,它被正确地设置为3。如果要重置计数器或将其设为特定的值,可以在类中添加一个setter方法。然而,在这个程序中并不需要这么做。