Objective-C 中的單例

其實對於單例,在程序開發中非常的常見。
最近我也對單例進行了一些更細緻的研究。
在我的程序中,單例一般是這麼去寫的:
+ (DataSource *)shareInstance
{
    static DataSource *instance = nil;
    if (instance == nil)
    {
        instance = [[DataSource alloc] init];
    }
    return instance;
}

在我做過的十幾個軟件中,一直都是這麼去寫的。
但是在我前段時間看書的過程中,發現跟書中的例子不太一樣,所以做了一下研究。

發現,這樣的單例是不可靠的。因爲如果開發者一直調用 [DataSource shareInstance] 當然沒有問題, 但是如果有人調用 [ DataSource alloc] 這樣的方法,就會出現多個對象。
所以,我們需要實現“嚴格”意義的單例。

需要克服兩個障礙:
1. 發起調用的對象不能以其他分配方式實例化單例對象。
2.對單例對象實例化的限制應該與引用計數內存模型共存。

下面我們看一下官方給出的單例模式的例子

static MyGizmoClass *sharedGizmoManager = nil;  
  
+ (MyGizmoClass*)sharedManager  
{  
    if (sharedGizmoManager == nil) {  
        sharedGizmoManager = [[super allocWithZone:NULL] init];  
    }  
    return sharedGizmoManager;  
}  
  
+ (id)allocWithZone:(NSZone *)zone  
{  
    return [[self sharedManager] retain];  
}  
  
- (id)copyWithZone:(NSZone *)zone  
{  
    return self;  
}  
  
- (id)retain  
{  
    return self;  
}  
  
- (NSUInteger)retainCount  
{  
    return NSUIntegerMax;  //denotes an object that cannot be released  
}  
  
- (void)release  
{  
    //do nothing  
}  
  
- (id)autorelease  
{  
    return self;  
}

他的方式是重寫release,retain,retainCount,autoRelease,allocWithZone和copyWithZone等方法。
1 重寫allocWithZone和copyWithZone的目的是外部在多次調用alloc的時候,內部能夠確保對象只創建了一次。
2 重寫release、retain、autorelease、retainCount避免單件對象被外部釋放。

兩個缺點:
A 隱藏了在對象生命週期管理時出現的bug。
    對對象的引用出錯的原因必然是程序本身的錯誤,隱藏對象的引用計數錯誤就是隱藏了應用程序的錯誤。
    從工程角度上講,崩潰要比程序的邏輯錯誤容易定位。
    解決方法:建議在release、retain、autorelease裏面做一些內部的調用次數監控,一旦發現外部調用不平衡就發出警告。
B 對象可以被多次init。
    多次調用init導致錯誤的可能性還是有的,這種錯誤包括重複加載某些資源降低性能。
    解決方法:重寫init並在內部判重就可以了。
C 多線程安全
    解決方法:在sharedManager中加入同步代碼塊,代碼:

+ (MyGizmoClass*)sharedManager  
{  
    @synchronized(self) {  
        if (sharedGizmoManager == nil) {  
            sharedGizmoManager = [[super allocWithZone:NULL] init];  
        }  
    }  
    return sharedGizmoManager;  
}  
發佈了17 篇原創文章 · 獲贊 1 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章