在《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的全局变量来作为类静态成员。
举个简单的例子:
[codesyntax lang=”objc” lines=”normal”]
//header file @interface Printer : NSObject -(void)print:(NSString *)content; @end //implementation file static int available; @implementation Printer + (void)initialize { available = 1; } - (id)init { if (available <= 0) { NSLog(@"No available printer"); return nil; } if (self = [super init]) { available--; } return self; } -(void)print:(NSString *)content { NSLog(@"%@", content); } -(void)dealloc { available++; [super dealloc]; } @end
[/codesyntax]
在我们的程序,我们有一个Printer
类可以构造对象来打印一些内容,但是Printer
不是无限,比如我们这里只有一个,于是我们就能构造出一个Printer
来,如果该对象没有被释放,那么我们就无法构造出另一个来进行打印。
[codesyntax lang=”objc” lines=”normal”]
#import <Foundation/Foundation.h> #import "Printer.h" int main(int argc, const char * argv[]) { @autoreleasepool { Printer *printer = [[Printer alloc] init]; [printer print:@"Print..."]; Printer *printer2 = [[Printer alloc] init]; NSLog(@"%@",printer2); [printer release]; printer2 = [[Printer alloc] init]; NSLog(@"%@",printer2); } return 0; }
[/codesyntax]
Print... No available printer (null)
从上边的例子,我们看出+(void)initialize
方法正确的为available
变量进行了初始化。
其实,static变量也可以定义在类方法的里面,这也是个实现的方法。
[codesyntax lang=”objc” lines=”normal”]
@implementation Printer + (int)available { static int available = 1; return available; } //other methods @end
[/codesyntax]
只是这样做有几个不便利的地方。第一,在我们的其它方法中如果想要使用available变量的时候,就不能直接写变量名,而要写成
Printer::available();
这样字代码就不那么简洁了。
第二,因为Objective-C的方法是没有访问域的约束的,所有方法实际上都是public的。虽然,如果我们不在@interface
中声明+ (int)available方
法,编译器会在该方法被调用时给出警告,但是因为@implementation
中定义了+ (int)available
方法,运行时依然可以执行并得到正确的返回结果。而且还可以 NSObject的- (id)performSelector:(SEL)aSelector
方法来规避警告。因此我们也就失去了静态变量的对外部的隐藏性。另一方面,因为我们还察觉到我们无法对方法内静态变量进行修改,于是又失去了类内部的共享性。
Objective-C中对于static变量,使用最多的地方,应该还是在单例模式(Singleton Patten)。比如上边Printer类我们实际只有一个,就可以定义单例方法。
[codesyntax lang=”objc” lines=”normal”]
@implementation Printer + (Printer *)instance { static Printer *instance = nil; if (!instance) { instance = [[Printer alloc] init]; } return instance; } //other methods @end
[/codesyntax]
不过个人认为将static Printer *instance = nil;
定义在方法外边作为全局变量,然后用+(void)initialize
进行初始化,+ (Printer *)instance
方法只返回变量会更好了。
[codesyntax lang=”objc” lines=”normal”]
static Printer *instance = nil; @implementation Printer + (void)initialize { if (!instance) { instance = [[Printer alloc] init]; } } + (Printer *)instance { return instance; } //other methods @end
[/codesyntax]
因为对于单例的初始化有线程安全的问题,而Apple的文档中明确指出+(void)initialize
调用是“in a thread-safe manner”。我们就不需要在+ (Printer *)instance
考虑线程安全性问题了。
另外,如果static变量是方法外部作为全局变量的话,那么它放在@implementaion
内还是外并没有关系,编译器都把它当做C的语法进行编译,并限定该变量是该文件内可访问。所以即使把static变量定义放在某个类的@implementaion
里面,假如该文件里还其他类的@implementaion
,依然可以访问到该static变量。
文章写得很棒。
虽是为说明问题,但文中的单例定义无法避免用 [Printer alloc]; 创建多个对象。
的确是无法避免多个对象得创建,不过这应该是Objective C语言得特性:方法无法隐藏。就像NSBundle,应该是只用mainBundle。所以只能写个注释:“Danger, do not call this method, otherwise you will be fired.”
Apple Doc 有个主题 “Creating a Singleton Instance” ,请参考。
I have to get across my respect for your kindness supporting persons that require help on your concern. Your very own dedication to passing the solution up and down ended up being exceptionally functional and has usually allowed ladies much like me to attain their aims. Your amazing invaluable tutorial implies much to me and even further to my office workers. Many thanks; from all of us.