使用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的全局變量來作爲類靜態成員。

舉個簡單的例子:

//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變量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章