單例 的意思就是確保在程序運行過程中只創(chuàng)建一個對象實例,而可以被多次使用榕酒。如我們常見的網(wǎng)絡(luò)請求類胚膊、工具類故俐、和其他管理類等想鹰。作為一個iOS開發(fā)者,我們經(jīng)常和單例打交道药版,比如:[NSUserDefaults standerUserDefaults]
, [UIApplication sharedApplication]
,[UIScreen mainScreen]
, [NSFileManager defaultManager]
返回的都是單例對象辑舷。
我們在開源項目、蘋果示例代碼能見到無數(shù)使用單例的例子槽片。Xcode 甚至有默認的 "Dispatch Once" 代碼片段何缓,使我們可以非常簡單地在代碼中添加一個單例。
一还栓、單例的作用
- 它可以保證某個類在程序運行過程中最多只有一個實例碌廓,節(jié)省內(nèi)存開銷。如果某個對象需要被多個其它對象使用剩盒,那可以考慮使用單例谷婆,因為這樣該類只使用一份內(nèi)存資源。
注意點A闪摹<涂妗!跟匆! 單例應(yīng)該只用來保存全局的狀態(tài)异袄,并且不能和任何作用域綁定。如果這些狀態(tài)的作用域比一個完整的應(yīng)用程序的生命周期要短玛臂,那么這個狀態(tài)就不應(yīng)該使用單例來管理烤蜕。因為單例從創(chuàng)建后到徹底關(guān)閉程序前都會一直存在,如果過多的創(chuàng)建單例無疑浪費系統(tǒng)資源和影響系統(tǒng)效率迹冤。
二讽营、單例的三要點
- 單例模式的三個要點:
1. 該類有且只有一個實例;(類的構(gòu)造方法是私有的我們只需要重寫allocWithZone
方法,讓初始化操作只執(zhí)行一次)
2. 該類必須能夠自行創(chuàng)建這個實例;(類提供一個類方法產(chǎn)生對象)
3. 該類必須能夠自行向整個系統(tǒng)提供這個實例叁巨。(類中有一個私有的自己對象我們可以在.m文件中定義一個屬性即可)
三斑匪、單例的實現(xiàn)步驟
1. 為單例對象創(chuàng)建一個靜態(tài)實例,可以寫成全局的锋勺,也可以在類方法里面實現(xiàn)蚀瘸,并初始化為nil;
2. 提供一個類方法庶橱,方便外界訪問贮勃;
3. 重寫allocWithZone
方法,用來保證其他人直接使用alloc和init試圖獲得一個新實力的時候不產(chǎn)生一個新實例苏章;
4. 重寫-copyWithZone
方法和-MutableCopyWithZone
方法
一般創(chuàng)建對象的步驟分為申請內(nèi)存(alloc)寂嘉、初始化(init)這兩個步驟奏瞬,我們要確保對象的唯一性,所以要在第一步攔截它泉孩。當我們調(diào)用alloc方法時硼端,oc內(nèi)部會調(diào)用allocWithZone這個方法來申請內(nèi)存,我們覆寫這個方法寓搬,然后在這個方法中調(diào)用shareInstance方法返回單例對象珍昨,這樣就可以達到我們的目的【渑纾拷貝對象也是同樣的原理镣典,覆寫copyWithZone方法,然后在這個方法中調(diào)用shareInstance方法返回單例對象唾琼。
#import "Tools.h"
@implementation Tools
// 創(chuàng)建靜態(tài)對象 防止外部訪問
static Tools *_instance;
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized (self) {
// 為了防止多線程同時訪問對象兄春,造成多次分配內(nèi)存空間,所以要加上線程鎖
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
return _instance;
}
// 為了使實例易于外界訪問 我們一般提供一個類方法
// 類方法命名規(guī)范 share類名|default類名|類名
+(instancetype)shareTools
{
//return _instance;
// 最好用self 用Tools他的子類調(diào)用時會出現(xiàn)錯誤
return [[self alloc]init];
}
// 為了嚴謹锡溯,也要重寫copyWithZone 和 mutableCopyWithZone
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
也可以使用Xcode 默認的dispatch_once
代碼片段用 GCD的方式創(chuàng)建:
@implementation Singleton
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedSingleton{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
@end
四赶舆、使用單例的注意事項
- 單例給我們帶來方便的同時也有一定的副作用,因為單例對象一旦創(chuàng)建趾唱,對象指針是保存在靜態(tài)區(qū)的涌乳,單例對象在堆中分配的內(nèi)存空間只有在程序終止后才會釋放,過多的單例必定會增大我們消耗的內(nèi)存
- 不要做斷開單例類對象與類中靜態(tài)引用的危險操作甜癞。
- 多線程使用單例使用共享資源時夕晓,注意線程安全問題。