在《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
來,如果該對象沒有被釋放,那么我們就無法構造出另一個來進行打印。
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
+(void)initialize
調用是
“in a thread-safe manner”。我們就不需要在
+ (Printer *)instance
考慮線程安全性問題了。
另外,如果static變量是方法外部作為全局變量的話,那么它放在@implementaion
內還是外並沒有關系,編譯器都把它當做C的語法進行編譯,並限定該變量是該文件內可訪問。所以即使把static變量定義放在某個類的@implementaion
里面,假如該文件里還其他類的@implementaion
,依然可以訪問到該static變量。