在iOS開發中,有很多地方都選擇使用單例模式。有很多時候必須要創建一個對象,并且不能創建多個,用單例就為了防止創建多個對象。單例模式的意思就是某一個類有且只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。這個類稱為單例類。
一、單例模式的三要點:
1. 該類有且只有一個實例;
2. 該類必須能夠自行創建這個實例;
3. 該類必須能夠自行向整個系統提供這個實例。
二、單例模式的優點與缺點:
1. 內存占用與運行時間
對比使用單例模式和非單例模式的例子,在內存占用與運行時間存在以下差距:
(1) 單例模式:單例模式每次獲取實例時都會先進行判斷,看該實例是否存在——如果存在,則返回;否則,則創建實例。因此,會浪費一些判斷的時間。但是,如果一直沒有人使用這個實例的話,那么就不會創建實例,節約了內存空間。
(2) 非單例模式:當類加載的時候就會創建類的實例,不管你是否使用它。然后當每次調用的時候就不需要判斷該實例是否存在了,節省了運行的時間。但是如果該實例沒有使用的話,就浪費了內存。
(3)實例控制:Singleton 會阻止其他對象實例化其自己的 Singleton 對象的副本,從而確保所有對象都訪問唯一實例。
靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程。
2. 線程的安全性
(1) 從線程的安全性上來講,不加同步的單例模式是不安全的。比如,有兩個線程,一個是線程A,另外一個是線程B,如果它們同時調用某一個方法,那就可能會導致并發問題。在這種情況下,會創建出兩個實例來,也就是單例的控制在并發情況下失效了。
(2) 非單例模式是線程安全的,因為程序保證只加載一次,在加載的時候不會發生并發情況。
(3) 單例模式如果要實現線程安全,只需要加上synchronized即可。但是這樣一來,就會減低整個程序的訪問速度,而且每次都要判斷,比較麻煩。
(4) 雙重檢查加鎖:為了解決(3)的繁瑣問題,可以使用“雙重檢查加鎖”的方式來實現,這樣,就可以既實現線程安全,又能使得程序性能不受太大的影響。
(4.1) 雙重檢查加鎖機制——并不是每次進入要調用的方法都需要同步,而是先不同步,等進入了方法之后,先檢查實例是否存在,如果不存在才進入下面的同步塊,這是第一重檢查。當進入同步塊后,再次檢查實例是否存在,如果不存在,就在同步的情況下創建一個實例,這是第二重檢查。這樣一來,就只需要同步一次,從而減少了多次在同步情況下進行判斷所浪費的時間。
(4.2) 雙重檢查加鎖機制的實現,會使用一個關鍵字volatile。它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內存的,從而確保了多個線程能正確的處理該變量。這種實現方式既可以實現線程安全地創建實例,而又不會對性能造成太大的影響。它只是在第一次創建實例的時候同步,以后就不需要同步了,從而加快了運行速度。
3. 單例模式會阻止其它對象實例化其自己的對象的副本,從而確保所有對象都訪問唯一實例。
4. 因為單例模式的類控制了實例化的過程,所以類可以更加靈活修改實例化過程。
三、iOS中的單例模式
1. 基本步驟:
(1) 為單例對象創建一個靜態實例,可以寫成全局的,也可以在類方法里面實現,并初始化為nil;
(2) 實現一個實例構造方法,檢查上面聲明的靜態實例是否為nil,如果是,則創建并返回一個本類的實例;
(3) 重寫allocWithZone方法,用來保證其他人直接使用alloc和init試圖獲得一個新實力的時候不產生一個新實例;
(4) 適當實現allocWitheZone,copyWithZone,release和autorelease。
//第一步:靜態實例,并初始化。
static SurveyRunTimeData *sharedObj = nil;
@implementation SurveyRunTimeData
//第二步:實例構造檢查靜態實例是否為nil
{
+ (SurveyRunTimeData*) sharedInstance
@synchronized (self)
{
if (sharedObj == nil)
{
[[self alloc] init];
}
}
return sharedObj;
}
//第三步:重寫allocWithZone方法
+ (id) allocWithZone:(NSZone *)zone
{
@synchronized (self) {
if (sharedObj == nil) {
sharedObj = [super allocWithZone:zone];
return sharedObj;
}
}
return nil;
}
//第四步
- (id) copyWithZone:(NSZone *)zone
{
return self;
}
//一下方法再Xcode5以上,已經不需要!大家根據事情情況自行判斷!
- (id) retain
{
return self;
}
- (unsigned) retainCount
{
return UINT_MAX;
}
- (oneway void) release
{
}
- (id) autorelease
{
return self;
}
- (id)init
{
@synchronized(self) {
[super init];//往往放一些要初始化的變量.
return self;
}
}
@end
+(id)allocWithZone:(NSZone *)zone{
@synchronized(self){
if (shareRootViewController == nil) {
shareRootViewController = [super allocWithZone:zone];
return shareRootViewController;
}
}
return nil;
}
NSZone: 簡單來說可以把它想象成一個內存池,alloc或者dealloc這些操作都是在這個內存池中操作的,cocoa總是會分配一個默認的nsZone,任何 默認內存操作都是在這個zone上進行的,使用默認zone存在缺陷,因為他是全局范圍的,頻繁使用會導致內存的碎片化,尤其是大量的alloc和 dealloc的時候,性能上會受到一定影響。因為你完全可以自己生成一個zone并將alloc,copy這些限制在這個zone中。
新聞熱點
疑難解答