在《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的全局變量來作爲類靜態成員。
舉個簡單的例子:
//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
在我們的程序,我們有一個Printer
類可以構造對象來打印一些內容,但是Printer
不是無限,比如我們這裏只有一個,於是我們就能構造出一個Printer
來,如果該對象沒有被釋放,那麼我們就無法構造出另一個來進行打印。
#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;
}
Print...
No available printer
(null)
從上邊的例子,我們看出+(void)initialize
方法正確的爲available
變量進行了初始化。
其實,static變量也可以定義在類方法的裏面,這也是個實現的方法。
@implementation Printer
+ (int)available {
static int available = 1;
return available;
}
//other methods
@end
只是這樣做有幾個不便利的地方。第一,在我們的其它方法中如果想要使用available變量的時候,就不能直接寫變量名,而要寫成
Printer::available();
這樣字代碼就不那麼簡潔了。
第二,因爲Objective-C的方法是沒有訪問域的約束的,所有方法實際上都是public的。雖然,如果我們不在@interface
中聲明+
(int)available方
法,編譯器會在該方法被調用時給出警告,但是因爲@implementation
中定義了+
(int)available
方法,運行時依然可以執行並得到正確的返回結果。而且還可以 NSObject的- (id)performSelector:(SEL)aSelector
方法來規避警告。因此我們也就失去了靜態變量的對外部的隱藏性。另一方面,因爲我們還察覺到我們無法對方法內靜態變量進行修改,於是又失去了類內部的共享性。
Objective-C中對於static變量,使用最多的地方,應該還是在單例模式(Singleton Patten)。比如上邊Printer類我們實際只有一個,就可以定義單例方法。
@implementation Printer
+ (Printer *)instance {
static Printer *instance = nil;
if (!instance) {
instance = [[Printer alloc] init];
}
return instance;
}
//other methods
@end
不過個人認爲將static Printer *instance = nil;
定義在方法外邊作爲全局變量,然後用+(void)initialize
進行初始化,+
(Printer *)instance
方法只返回變量會更好了。
static Printer *instance = nil;
@implementation Printer
+ (void)initialize {
if (!instance) {
instance = [[Printer alloc] init];
}
}
+ (Printer *)instance {
return instance;
}
//other methods
@end
因爲對於單例的初始化有線程安全的問題,而Apple的文檔中明確指出+(void)initialize
調用是“in a thread-safe
manner”。我們就不需要在+ (Printer *)instance
考慮線程安全性問題了。
另外,如果static變量是方法外部作爲全局變量的話,那麼它放在@implementaion
內還是外並沒有關係,編譯器都把它當做C的語法進行編譯,並限定該變量是該文件內可訪問。所以即使把static變量定義放在某個類的@implementaion
裏面,假如該文件裏還其他類的@implementaion
,依然可以訪問到該static變量。