一、單例介紹
單例模式:單例模式,屬于創(chuàng)建類型的一種常用的軟件設計模式。通過單例模式的方法創(chuàng)建的類在當前進程中只有一個實例。
為了我們能更好的理解單例模式,我列舉以下幾個cocoa框架中常用的單例:
1.UIApplication:應用程序。一個UIApplication對象就代表著一個應用程序,每個應用程序有且僅有一個UIApplication對象,開發(fā)中最常用的是使用它的openURL函數(shù)來跳轉到其他應用程序,通過 [UIApplication sharedApplication] 類方法可以獲得。
2.NSNotificationCenter:通知中心。iOS中的通知中心是一種消息廣播,采用觀察者模式和單例模式,一個應用有且僅有一個通知中心。通過 [NSNotificationCenter defaultCenter] 類方法可以獲得。
3.NSFileManager:文件管理器。它是iOS文件系統(tǒng)的接口,用來創(chuàng)建、修改、訪問文件。一個應用有且僅有一個文件管理器。通過 [NSFileManager defaultManager] 類方法可以獲得。
4.NSUserDefaults:用戶偏好設置。它主要用來存儲簡單的鍵值對數(shù)據(jù),數(shù)據(jù)持久化最簡單和基礎的一種方案。通過 [NSUserDefaults standardUserDefaults] 類方法可以獲得。
5.NSURLCache:URL緩存。通過將NSURLRequest對象映射到NSCachedURLResponse對象來實現(xiàn)對URL加載請求的響應的緩存。通過 [NSURLCache sharedURLCache] 類方法可以獲得。
1.1 單例模式的要點
1.只能有一個實例;
2.它必須自行創(chuàng)建這個實例;
3.它必須自行向整個系統(tǒng)提供這個實例。
從具體實現(xiàn)角度來說,是以下三點:
1.單例模式的類只提供私有的構造函數(shù);
2.類定義中含有一個該類的靜態(tài)私有對象(實例);
3.提供一個靜態(tài)的公有函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對象(實例)。
1.2 單例模式的優(yōu)點
1.實例控制:單例可以保證系統(tǒng)中該類有且僅有一個實例,確保所有對象都訪問這個唯一實例;
2.靈活性:因為類控制了實例化的過程,所以類可以靈活更改實例化過程;
3.節(jié)省開銷:因為只有一個實例,所以減少內(nèi)存開發(fā)和系統(tǒng)的性能開銷。
1.3 單例模式的缺點
1.由于單例模式中沒有抽象層,可擴展性比較差。
2.實例一旦被創(chuàng)造,對象指針保存在靜態(tài)區(qū),那么在堆區(qū)分配的空間只有在App結束后才會被釋放;
3.單例類職責過重,在一定程度上違背了“單一職責原則”。
4.濫用單例會帶來一些負面問題,比如,單例會隱性地讓毫不相關的類產(chǎn)生耦合等問題。
二、單例的實現(xiàn)
單例的實現(xiàn)重點就是防止在外部調(diào)用的時候出現(xiàn)多個不同的實例,也就是說要從創(chuàng)建的方式入手禁止出現(xiàn)多個不同的實例。
主要做到以下幾點:
1.防止調(diào)用 [[A alloc] init] 引起錯誤
2.防止調(diào)用 new 引起錯誤
3.防止調(diào)用 copy 引起錯誤
4.防止調(diào)用 mutableCopy 引起錯誤
2.1 典型的單例寫法
static id sharedMyManager;
+(id)shareThemeManager{
if(sharedThemeManager == nil){
shareMyManager = [[self alloc]init];
}
return sharedMyManager;
}
缺點:無法保證多線程情況下只創(chuàng)建一個對象。適用于只有單線程。
2.2 加鎖的寫法
static Singleton *_sharedSingleton = nil;
+(instancetype)sharedSingleton {
@synchronized(self){ //加鎖,保證多線程下也只能有一個線程進入
if (! _sharedSingleton) {
_sharedSingleton = [[self alloc] init];
}
}
return _sharedSingleton;
}
2.3 GCD寫法【常用】
2.3.1 重寫父類方法
static Singleton *_sharedSingleton = nil; + (instancetype)sharedSingleton { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 不能再使用 alloc 方法 // 因為已經(jīng)重寫了 allocWithZone 方法,所以這里要調(diào)用父類的分配空間的方法 _sharedSingleton = [[super allocWithZone:NULL] init]; }); return _sharedSingleton; } // ②、防止 [[A alloc] init] 和 new 引起的錯誤。因為 [[A alloc] init] 和 new 實際是一樣的工作原理,都是執(zhí)行了下面方法 + (instancetype)allocWithZone:(struct _NSZone *)zone { return [Singleton sharedSingleton]; } // ③、NSCopying 防止 copy 引起的錯誤。當你的單例類不遵循 NSCopying 協(xié)議,外部調(diào)用本身就會出錯. - (id)copyWithZone:(nullable NSZone *)zone { return [Singleton sharedSingleton]; } // ④、防止 mutableCopy 引起的錯誤,當你的單例類不遵循 NSMutableCopying 協(xié)議,外部調(diào)用本身就會出錯. - (id)mutableCopyWithZone:(nullable NSZone *)zone { return [Singleton sharedSingleton]; } dispatch_once 主要是根據(jù) onceToken 的值來決定怎么去執(zhí)行代碼。
1.當 onceToken = 0 時,線程執(zhí)行 dispatch_once 的 block 中代碼;
2.當 onceToken = -1 時,線程跳過 dispatch_once 的 block 中代碼不執(zhí)行;
3.當 onceToken 為其他值時,線程被阻塞,等待 onceToken 值改變。
當線程調(diào)用 shareInstance,此時 onceToken = 0,調(diào)用 block 中的代碼,此時 onceToken = 其他值。當其他線程再調(diào)用 shareInstance 方法時,onceToken為其他值,線程阻塞。當 block 線程執(zhí)行完 block 之后,onceToken = -1,其他線程不再阻塞,跳過 block。下次在調(diào)用 shareInstance 時, block 已經(jīng)為 -1,直接跳過 block。
2.3.2 禁止外部調(diào)用
這種寫法還蠻簡單好用的~
.h 文件
- (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; - (id)copy NS_UNAVAILABLE; - (id)mutableCopy NS_UNAVAILABLE;
.m 文件
-
static Singleton *_sharedSingleton = nil; + (instancetype)sharedSingleton { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedSingleton = [[self alloc] init]; // 要使用 self 來調(diào)用 }); return _sharedSingleton; }
當運行 init 或者 new 時,會報錯 'init' is unavailable 或者 'new' is unavailable。
2.3.3 宏定義寫法
寫成宏是比較方便也比較常見的一種:
#define MY_SINGLETON_DEF(_type_) + (_type_ *)sharedInstance; \
+(instancetype) alloc __attribute__((unavailable("call sharedInstance instead"))); \
+(instancetype) new __attribute__((unavailable("call sharedInstance instead"))); \
-(instancetype) copy __attribute__((unavailable("call sharedInstance instead"))); \
-(instancetype) mutableCopy __attribute__((unavailable("call sharedInstance instead"))); \
#define MYY_SINGLETON_IMP(_type_) + (_type_ *)sharedInstance{ \
static _type_ * sharedInstance = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
sharedInstance = [[super alloc] init]; \
}); \
return sharedInstance; \
}
用法也很簡單:
//引用
@interface MYSingleton : NSObject
MY_SINGLETON_DEF(MYSingleton);
@end
//實現(xiàn)
@implementation MYSingleton
MY_SINGLETON_IMP(MYSingleton);
@end
2.4 免鎖寫法
static Singleton *_sharedSingleton = nil;
+ (instancetype)sharedSingleton {
static BOOL initialized = NO;
if (initialized == NO){
initialized = YES;
_sharedSingleton = [[self alloc] init];
}
return _sharedSingleton;
}
1,2,4三種寫法還需將 init new 重寫或禁用才算完整哦~
三、單例的濫用
上面關于單例的部分,寫的太好了,所以我把全文搬運過來了
原文地址:iOS 單例模式詳解/避免濫用單例
有興趣的可以去看看