天天看點

ObjectiveC單例

其實對于單例,在程式開發中非常的常見。

最近我也對單例進行了一些更細緻的研究。

在我的程式中,單例一般是這麼去寫的:

+ (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;  

}  

繼續閱讀