什么是單例模式
- 單例模式就是要保證系統(tǒng)中一個(gè)類只有一個(gè)對(duì)象實(shí)例。無論用什么方法創(chuàng)建多少次搀突,所得的對(duì)象都是同一個(gè)對(duì)象。
單例模式的應(yīng)用場(chǎng)景
- 在iOS開發(fā)中,我們已經(jīng)遇到過很多單例模式的身影:
- [UIApplication sharedApplication]饲帅、[NSUserDefaults standardUserDefaults]、[NSNotificationCenter defaultCenter] 等等瘤泪。
- 音樂播放器中用于播放音樂的播放器對(duì)象灶泵、一個(gè)APP中用于保存并且能夠隨時(shí)方便地獲取的用戶信息的對(duì)象 等等。
單例模式的關(guān)鍵
對(duì)象只創(chuàng)建一次
可供全局訪問
不會(huì)被釋放对途,直至程序結(jié)束
單例模式的分析與實(shí)現(xiàn)
- 對(duì)象只創(chuàng)建一次:
在iOS中我們創(chuàng)建一個(gè)對(duì)象一般用:alloc init 或 new赦邻,其中new方法內(nèi)部實(shí)際也是通過alloc init創(chuàng)建的,所以我們把注意力放在alloc init上实檀。首先alloc方法是給對(duì)象分配內(nèi)存空間惶洲,然后init方法是對(duì)該對(duì)象進(jìn)行初始化,所以想要控制對(duì)象的創(chuàng)建關(guān)鍵是在alloc方法上膳犹,又由于alloc默認(rèn)是調(diào)用allocWithZone方法恬吕,所以我們應(yīng)該重寫allocWithZone方法來控制對(duì)象只能創(chuàng)建一次:
id instance; // 定義全局變量來保存單例對(duì)象,此處還不完善须床,后面還會(huì)提到
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 此處if判斷可以避免頻繁加鎖铐料,只要對(duì)象已創(chuàng)建就直接返回,亦相當(dāng)于懶加載
if (instance == nil) {
// 方法一:互斥鎖方式
@synchronized(self) {
if (instance == nil) {
instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
}
}
// 方法二:GCD一次性代碼方式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone]; // 用super去調(diào)用钠惩,避免死循環(huán)
});
}
return instance;
}
-
可供全局訪問:
單例模式要提供一個(gè)類方法來獲取單例對(duì)象柒凉,例如:Tools *tools = [Tools sharedTools]; UserTool *userTool = [UserTool defaultUserTool];
實(shí)現(xiàn)如下:
// 單例類方法 命名規(guī)則: shared + 類名 或 default + 類名 + (instancetype)sharedTools { if (instance == nil) { instance = [self alloc] init]; // 最終還是調(diào)用allocWithZone方法 } return instance; }
-
不會(huì)被釋放,直至程序結(jié)束:
在第一個(gè)關(guān)鍵點(diǎn)中妻柒,我們定義了一個(gè)全局變量id instance;
來保存我們創(chuàng)建的單例對(duì)象扛拨,但是有個(gè)弊端,如果在別的文件中(別的類)使用extern關(guān)鍵字來獲取這個(gè)對(duì)象是可以拿到的举塔,并且可以把該對(duì)象銷毀绑警,例如:extern id instance; instance = nil;
這樣以來,下次再獲取單例對(duì)象的時(shí)候發(fā)現(xiàn)為nil就會(huì)重新創(chuàng)建對(duì)象央渣,即二次創(chuàng)建對(duì)象计盒,亦即不為單例模式,為了防止單例對(duì)象的銷毀芽丹,我們應(yīng)該使用static修飾用于保存單例對(duì)象的變量北启,限制變量的作用域?yàn)榇宋募捎茫敲磩e的文件(別的類)就無法拿到這個(gè)對(duì)象拔第,從而達(dá)到單例對(duì)象不會(huì)被釋放咕村。
即把id instance;
改為static id instance;
嚴(yán)謹(jǐn)?shù)膯卫J?/h2>
-
創(chuàng)建對(duì)象除了alloc init 和 new 以外,還可以通過copy 和 mutableCopy來創(chuàng)建對(duì)象蚊俺,為了嚴(yán)謹(jǐn)起見懈涛,我們還需要控制這兩個(gè)方法的創(chuàng)建過程,即需要重寫copyWithZone和mutableCopyWithZone方法泳猬,重寫這兩個(gè)方法需要分別遵守NSCopying 和 NSMutableCopying協(xié)議批钠。
因?yàn)檫@兩個(gè)方法是對(duì)象方法,所以當(dāng)想要使用這兩個(gè)方法來創(chuàng)建新對(duì)象的時(shí)候得封,只能是用單例對(duì)象來調(diào)用此方法埋心,即單例對(duì)象已經(jīng)創(chuàng)建了,所以我們只需要返回我們保存的單例對(duì)象即可忙上,代碼如下:
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return instance;
}
至此一個(gè)嚴(yán)謹(jǐn)?shù)膯卫O(shè)計(jì)模式已經(jīng)完成了拷呆,下面附上完整代碼:
#import "Tools.h"
@implementation Tools
static id instance;
+ (instancetype)sharedTools {
if (instance == nil) {
instance = [[self alloc] init];
}
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 此處if判斷可以避免頻繁加鎖,只要對(duì)象已創(chuàng)建就直接返回疫粥,亦相當(dāng)于懶加載
if (instance == nil) {
// 方法一:互斥鎖方式
@synchronized(self) {
if (instance == nil) {
instance = [super allocWithZone:zone]; // 用super去調(diào)用洋腮,避免死循環(huán)
}
}
// 方法二:GCD一次性代碼方式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
});
}
return instance;
}
// 遵守NSCopying協(xié)議
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
// 遵守NSMutableCopying協(xié)議
- (id)mutableCopyWithZone:(NSZone *)zone {
return instance;
}
@end
參考文章
創(chuàng)建對(duì)象除了alloc init 和 new 以外,還可以通過copy 和 mutableCopy來創(chuàng)建對(duì)象蚊俺,為了嚴(yán)謹(jǐn)起見懈涛,我們還需要控制這兩個(gè)方法的創(chuàng)建過程,即需要重寫copyWithZone和mutableCopyWithZone方法泳猬,重寫這兩個(gè)方法需要分別遵守NSCopying 和 NSMutableCopying協(xié)議批钠。
因?yàn)檫@兩個(gè)方法是對(duì)象方法,所以當(dāng)想要使用這兩個(gè)方法來創(chuàng)建新對(duì)象的時(shí)候得封,只能是用單例對(duì)象來調(diào)用此方法埋心,即單例對(duì)象已經(jīng)創(chuàng)建了,所以我們只需要返回我們保存的單例對(duì)象即可忙上,代碼如下:
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return instance;
}
#import "Tools.h"
@implementation Tools
static id instance;
+ (instancetype)sharedTools {
if (instance == nil) {
instance = [[self alloc] init];
}
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 此處if判斷可以避免頻繁加鎖,只要對(duì)象已創(chuàng)建就直接返回疫粥,亦相當(dāng)于懶加載
if (instance == nil) {
// 方法一:互斥鎖方式
@synchronized(self) {
if (instance == nil) {
instance = [super allocWithZone:zone]; // 用super去調(diào)用洋腮,避免死循環(huán)
}
}
// 方法二:GCD一次性代碼方式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
});
}
return instance;
}
// 遵守NSCopying協(xié)議
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
// 遵守NSMutableCopying協(xié)議
- (id)mutableCopyWithZone:(NSZone *)zone {
return instance;
}
@end