IOS開發模式-單例模式
正文
其實小編在接觸了蘋果的新的開發語言swift之后,深刻覺得單例模式在swift里面體現的淋漓盡致,不過在OC里面確實會有一些麻煩。簡單對比一下OC的單例模式和swift的單例模式吧。拿小編之前的一個程序開刀。
struct DeviceData {
static let height = UIScreen.mainScreen().bounds.height
static let width = UIScreen.mainScreen().bounds.width
static let bound = UIScreen.mainScreen().bounds
} 再來看一下oc版本的單例
@implementation HMMusicTool
static id _instance;
/**
* alloc方法內部會調用這個方法
*/
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
+ (instancetype)sharedMusicTool
{
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [[self alloc] init];
}
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
@end 這時候swift的優勢可見一斑吧。不過小編還是會辯證的去看待問題的,swift還是存在一些兼容性的問題的。這次小編先拿oc開刀。底層實現都是一樣的。
簡單介紹單例模式 防止大家百度,小編直接搬來了爛大街的介紹
單例模式的意思就是只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。這個類稱為單例類。
1.單例模式的要點:
顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
2.單例模式的優點:
1.實例控制:Singleton 會阻止其他對象實例化其自己的 Singleton 對象的副本,從而確保所有對象都訪問唯一實例。2.靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程
那么如何優雅的實現oc的單例模式呢。
ARC + 互斥鎖版本
第一步。 為單例對象實現一個靜態實例,并初始化,然后設置成nil
第二步。檢查靜態實例的值是否為nil
第三步。重寫 allocWithZone, copyWithZone 。
第四步。加鎖
代碼如下:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
+ (instancetype)sharedMusicTool
{
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [[self alloc] init];
}
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
@end 調用
HMMusicTool *tool = [[HMMusicTool alloc] init]; HMMusicTool *tool2 = [[HMMusicTool alloc] init]; HMMusicTool *tool3 = [HMMusicTool sharedMusicTool]; HMMusicTool *tool4 = [HMMusicTool sharedMusicTool];
tool。tool2.tool3.tool4的地址是一致的。簡單分析一下上面的程序。類HMMusicTool在alloc的時候會調用 allocWithZone方法。所以重寫 allocWithZone方法。
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [super allocWithZone:zone];
}
} 判斷是否創建多次。然后加一個互斥鎖,防止在多線程中創建多個對象,返回對象_instance.
除了通過alloc創建之外,還可以通過類方法創建,典型的創建方法例如 [ UIApplication sharedApplication ]然后我們創建一個類方法。
+ (instancetype)sharedMusicTool
{
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止創建多次
_instance = [[self alloc] init];//如果第一次使用。調用allocWithZone。
}
}
}
return _instance;
} 當然除了以上調用方式外。還有可能通過copy實現對象的復制。還需要重寫copy方法。這時候需要遵循NSCoping協議。
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
} 當調用copyWithZone的時候,往往已經實例化了一個變量。這時候_instance是存在的。所以直接返回即可。
為什么_instance要加上static呢。
_instance作為全局變量。如果不加上static。則在程序的其他文件里面可以通過extern訪問。對_instance的值進行更改,這一點是非常危險的。但是加上static。_instance只能在當前文件訪問。其他文件訪問出錯。
ARC + GCD版本
相對互斥鎖版本沒有相差太多,只是使用了GCD的一個方法。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
<#code to be executed once#>
}); 閉包里面的代碼只能被執行一次。所以更改后的程序為
// 用來保存唯一的單例對象
static id _instace;
+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
+ (instancetype)sharedDataTool
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instace;
}
@end 非ARC版本 單例模式
非ARC版本需要手動管理內存,所以單例模式需要防止單例被釋放,這時候需要重寫幾個方法
- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;} 這樣子就防止了單例被釋放,保持單例的計數為1。全部代碼為GCD+非ARC
static id _instace;
+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
+ (instancetype)sharedDataTool
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instace;
}
- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;} 總結
單例在程序開發當中,有著舉足輕重的地位。當有多個單例對象需要創建的時候,可以考慮把單例抽出來作為宏,不過相對來說調試來說確實會比較坑。不過一切為了簡單嘛。如下
// .h文件
#define HMSingletonH(name) + (instancetype)shared##name;
// .m文件
#define HMSingletonM(name) \
static id _instance; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}