使用Objective-C的+(void)initialize初始化static变量
在《Objective C类方法load和initialize的区别》一文中,我介绍了Objective-C对待+(void)initialize
和+(void)load
两个方法在编译和执行时出现的不同。而这些不同也是在使用时应该非常注意的地方。不过文章里面我没有讲这两个方法在Objective-C中究竟有什么实用价值。
其实+(void)initialize
可以视为C#,Java中的静态构造函数。有了这个方法,我们就不用像C++自己另找途径来设计静态构造函数了。不过Objective-C中又有一些很不同的地方,因为Objective-C里不能把数据成员限定为static或者const。也就是说,虽然Objective-C可以定义类方法,但是类不能有数据成员。所以也不存在静态数据成员初始化的问题。
不过作为C语言的超集,Objective-C依然可以沿用C的一些特点了定义static的全局变量来作为类静态成员。
举个简单的例子:
1 //header file 2 @interface Printer : NSObject 3 -(void)print:(NSString *)content; 4 @end 5 6 //implementation file 7 static int available; 8 @implementation Printer 9 10 + (void)initialize { 11 available = 1; 12 } 13 14 - (id)init { 15 if (available <= 0) { 16 NSLog(@"No available printer"); 17 return nil; 18 } 19 20 if (self = [super init]) { 21 available--; 22 } 23 return self; 24 } 25 26 -(void)print:(NSString *)content { 27 NSLog(@"%@", content); 28 } 29 30 -(void)dealloc { 31 available++; 32 [super dealloc]; 33 } 34 35 @end
在我们的程序,我们有一个Printer
类可以构造对象来打印一些内容,但是Printer
不是无限,比如我们这里只有一个,于是我们就能构造出一个Printer
来,如果该对象没有被释放,那么我们就无法构造出另一个来进行打印。
1 #import <Foundation/Foundation.h> 2 #import "Printer.h" 3 4 int main(int argc, const char * argv[]) 5 { 6 @autoreleasepool { 7 Printer *printer = [[Printer alloc] init]; 8 [printer print:@"Print..."]; 9 Printer *printer2 = [[Printer alloc] init]; 10 NSLog(@"%@",printer2); 11 [printer release]; 12 printer2 = [[Printer alloc] init]; 13 NSLog(@"%@",printer2); 14 } 15 return 0; 16 }
Print... No available printer (null)
从上边的例子,我们看出+(void)initialize
方法正确的为available
变量进行了初始化。
其实,static变量也可以定义在类方法的里面,这也是个实现的方法。
1 @implementation Printer 2 3 + (int)available { 4 static int available = 1; 5 return available; 6 } 7 8 //other methods 9 10 @end
只是这样做有几个不便利的地方。第一,在我们的其它方法中如果想要使用available变量的时候,就不能直接写变量名,而要写成
Printer::available();
这样字代码就不那么简洁了。
第二,因为Objective-C的方法是没有访问域的约束的,所有方法实际上都是public的。虽然,如果我们不在@interface
中声明+ (int)available方
法,编译器会在该方法被调用时给出警告,但是因为@implementation
中定义了+ (int)available
方法,运行时依然可以执行并得到正确的返回结果。而且还可以 NSObject的- (id)performSelector:(SEL)aSelector
方法来规避警告。因此我们也就失去了静态变量的对外部的隐藏性。另一方面,因为我们还察觉到我们无法对方法内静态变量进行修改,于是又失去了类内部的共享性。
Objective-C中对于static变量,使用最多的地方,应该还是在单例模式(Singleton Patten)。比如上边Printer类我们实际只有一个,就可以定义单例方法。
1 @implementation Printer 2 3 + (Printer *)instance { 4 static Printer *instance = nil; 5 if (!instance) { 6 instance = [[Printer alloc] init]; 7 } 8 9 return instance; 10 } 11 //other methods 12 13 @end
不过个人认为将static Printer *instance = nil;
定义在方法外边作为全局变量,然后用+(void)initialize
进行初始化,+ (Printer *)instance
方法只返回变量会更好了。
1 static Printer *instance = nil; 2 3 @implementation Printer 4 + (void)initialize { 5 if (!instance) { 6 instance = [[Printer alloc] init]; 7 } 8 } 9 + (Printer *)instance { 10 return instance; 11 } 12 //other methods 13 @end
因为对于单例的初始化有线程安全的问题,而Apple的文档中明确指出+(void)initialize
调用是“in a thread-safe manner”。我们就不需要在+ (Printer *)instance
考虑线程安全性问题了。
另外,如果static变量是方法外部作为全局变量的话,那么它放在@implementaion
内还是外并没有关系,编译器都把它当做C的语法进行编译,并限定该变量是该文件内可访问。所以即使把static变量定义放在某个类的@implementaion
里面,假如该文件里还其他类的@implementaion
,依然可以访问到该static变量。