Thousand Squared

Singletons in Cocoa/Objective-C

| Comments

什麼是Singleton?

在程式設計中,singleton是常常被使用的design pattern。

依照官方文件來解釋,它的意思是說:

A singleton class returns the same instance no matter how many times an application requests it.

如果你建立了一個 singleton class,當你在呼叫此 class 的 instance 時,它永遠會回傳相同的 instance。這樣的好處在於,你不會建立無用的 instance,不浪費記憶體,也不會產生錯誤的資料,在 application 中永遠都使用同一筆資料。

建立Singleton

依照官方的建議 Creating a Singleton Instance

嚴格定義一個 singleton class,你必須要斷絕使用者任何可能產生錯誤的步驟。

方法1:

我們建立一個SingletonClass,實作它的.h與.m檔。

SingletonClass.h
@interface SingletonClass : NSObject
+ (SingletonClass *)sharedInstance;
@end
SingletonClass.m
@implementation SingletonClass

+ (id)sharedInstance
{
    static SingletonClass *sharedMyInstance = nil;
    
    if (!sharedMyInstance) {
        sharedMyInstance = [[super allocWithZone:nil] init];
    }
    
    return sharedMyInstance;
}
@end

+ (id)allocWithZone:(NSZone *)zone
{
    return [self sharedInstance];
}

此方法主要利用static的概念實作:一個變數被宣告成 static,此變數不會隨著 method 結束而被銷毀。更進一步的說,static 變數只會被宣告一次,而且永遠不會被銷毀。而當你把一個 static 變數宣告在 method 裡時,只有此 method 才可以呼叫此 static 變數。

照上面的例子來看,只有sharedInstance這個method可以呼叫sharedMyInstance。此外當第二次遇到static SingletonClass *sharedMyInstance = nil;時,程式會發現,已經有 sharedMyInstance了,所以就不做任何動作(因為 static 變數無法被銷毀)。

那為什麼要override allocWithZone:??

Program defensively!!!

因為要防止聰明的 programmer,透過allocWithZone:產生新的 instance。

當使用者呼叫allocWithZone時,程式會改呼叫sharedInstance,判斷是否已經產生sharedMyInstance。這樣就達到singleton的效果!

但是,此種寫法有個致命的缺點,它 不是thread safe,也就是說,當你的 instance 同時被許多的 thread 呼叫時,有可能會爆炸...

如果你一定要用 singleton,就用 dispatch_once() 吧!

方法2:

SingletonClass.m
@implementation SingletonClass

+ (id)sharedInstance
{
    static SingletonClass *sharedMyInstance = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedMyInstance = [[self alloc] initWithSingleton];
    });
    
    return sharedMyInstance;
}
@end

+ (id)allocWithZone:(NSZone *)zone
{
    return [self sharedInstance];
}

- (id)initWithSingleton
{
        self = [super init];
    if (self)
    {
        // init your properties
    }
}

官方文件檔對 dispatch_once 的解釋:

Executes a block object once and only once for the lifetime of an application.

方法2是目前最簡單也最安全,也是常在WWDC影片中看到的方法。

結論:

如果真的要用singleton,那就使用第二種方法吧!

參考資料:

  1. Singleton - Cocoa Core Competencies.
  2. Creating a Singleton Instance - Cocoa Fundamentals Guide.
  3. Singletons in Cocoa/Objective-C
  4. Singletons: You're doing them wrong

Comments

comments powered by Disqus