單例模式的作用
可以保證在程序運(yùn)行過程,一個(gè)類只有一個(gè)實(shí)例浴鸿,而且該實(shí)例易于供外界訪問
從而方便地控制了實(shí)例個(gè)數(shù)匣沼,并節(jié)約系統(tǒng)資源
單例模式的使用場(chǎng)合
在整個(gè)應(yīng)用程序中瘪板,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)
單例模式在ARC\MRC環(huán)境下的寫法有所不同,需要編寫2套不同的代碼
可以用宏判斷是否為ARC環(huán)境
if __has_feature(objc_arc)
// ARC
else
// MRC
endif
單例模式 - ARC
ARC中揩慕,單例模式的實(shí)現(xiàn)
在.m中保留一個(gè)全局的static的實(shí)例
static id _instance;
重寫allocWithZone:方法亭畜,在這里創(chuàng)建唯一的實(shí)例(注意線程安全)
(id)allocWithZone:(struct
_NSZone *)zone {
if (_instance == nil) { //
防止頻繁加鎖
@synchronized(self) {
if
(_instance== nil) { //
防止創(chuàng)建多次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
提供1個(gè)類方法讓外界訪問唯一的實(shí)例
(instancetype)sharedMusicTool {
if (_instance == nil) { //
防止頻繁加鎖
@synchronized(self) {
if
(_instance== nil) { //
防止創(chuàng)建多次
_instance
= [[self alloc]
init];
}
}
}
return _instance;
}
實(shí)現(xiàn)copyWithZone:方法
(id)copyWithZone:(struct
_NSZone *)zone {
return _instance;
}
單例模式 – 非ARC
非ARC中(MRC),單例模式的實(shí)現(xiàn)(比ARC多了幾個(gè)步驟)
實(shí)現(xiàn)內(nèi)存管理方法
(id)retain { return
self; }
(NSUInteger)retainCount {return 1; }
(oneway void)release {}
- (id)autorelease { return
self; }
************************筆記****************************
1.單例模式
- 1.1 概念相關(guān)
(1)單例模式
在程序運(yùn)行過程迎卤,一個(gè)類只有一個(gè)實(shí)例
(2)使用場(chǎng)合
在整個(gè)應(yīng)用程序中拴鸵,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)
- 1.2
ARC實(shí)現(xiàn)單例
(1)步驟
01 在類的內(nèi)部提供一個(gè)static修飾的全局變量
02 提供一個(gè)類方法,方便外界訪問
03 重寫+allocWithZone方法蜗搔,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
04 嚴(yán)謹(jǐn)起見劲藐,重寫-copyWithZone方法和-MutableCopyWithZone方法
(2)相關(guān)代碼
提供一個(gè)static修飾的全局變量,強(qiáng)引用著已經(jīng)實(shí)例化的單例對(duì)象實(shí)例
static XMGTools *_instance;
類方法碍扔,返回一個(gè)單例對(duì)象
+(instancetype)shareTools
{
注意:這里建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self
alloc]init];
}
保證永遠(yuǎn)只分配一次存儲(chǔ)空間
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
使用GCD中的一次性代碼
static
dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
_instance
= [super allocWithZone:zone];
});
使用加鎖的方式瘩燥,保證只分配一次存儲(chǔ)空間
@synchronized(self) {
if (_instance ==nil) {
_instance = [super
allocWithZone:zone];
}
}
return _instance;
}
mutableCopy 創(chuàng)建一個(gè)新的可變對(duì)象,并初始化為原對(duì)象的值不同,新對(duì)象的引用計(jì)數(shù)為1厉膀;
copy 返回一個(gè)不可變對(duì)象。分兩種情況:(1)若原對(duì)象是不可變對(duì)象二拐,那么返回原對(duì)象服鹅,并將其引用計(jì)數(shù)加1;(2)若原對(duì)象是可變對(duì)象百新,那么創(chuàng)建一個(gè)新的不可變對(duì)象企软,并初始化為原對(duì)象的值,新對(duì)象的引用計(jì)數(shù)為1饭望。
讓代碼更加的嚴(yán)謹(jǐn)
-(nonnull id)copyWithZone:(nullable
NSZone *)zone
{
return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
- 1.3
MRC實(shí)現(xiàn)單例
(1)實(shí)現(xiàn)步驟
01 在類的內(nèi)部提供一個(gè)static修飾的全局變量
02 提供一個(gè)類方法仗哨,方便外界訪問
03 重寫+allocWithZone方法,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
04 嚴(yán)謹(jǐn)起見铅辞,重寫-copyWithZone方法和-MutableCopyWithZone方法
05 重寫release方法
06 重寫retain方法
07 建議在retainCount方法中返回一個(gè)最大值
(2)配置MRC環(huán)境知識(shí)
01 注意ARC不是垃圾回收機(jī)制厌漂,是編譯器特性
02 配置MRC環(huán)境:build
setting ->搜索automatic ref->修改為NO
(3)相關(guān)代碼
提供一個(gè)static修飾的全局變量,強(qiáng)引用著已經(jīng)實(shí)例化的單例對(duì)象實(shí)例
static XMGTools *_instance;
類方法斟珊,返回一個(gè)單例對(duì)象
+(instancetype)shareTools
{
注意:這里建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self
alloc]init];
}
保證永遠(yuǎn)只分配一次存儲(chǔ)空間
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
使用GCD中的一次性代碼
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
使用加鎖的方式苇倡,保證只分配一次存儲(chǔ)空間
@synchronized(self) {
if (_instance ==nil) {
_instance = [super
allocWithZone:zone];
}
}
return _instance;
}
讓代碼更加的嚴(yán)謹(jǐn)
-(nonnull id)copyWithZone:(nullable
NSZone *)zone
{
return
[[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
在MRC環(huán)境下,如果用戶retain了一次,那么直接返回instance變量旨椒,不對(duì)引用計(jì)數(shù)器+1
如果用戶release了一次晓褪,那么什么都不做,因?yàn)閱卫J皆谡麄€(gè)程序運(yùn)行過程中都擁有且只有一份综慎,程序退出之后被釋放涣仿,所以不需要對(duì)引用計(jì)數(shù)器操作
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
慣用法,有經(jīng)驗(yàn)的程序員通過打印retainCount這個(gè)值可以猜到這是一個(gè)單例
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
- 1.4 通用版本
(1)有意思的對(duì)話
01 問:寫一份單例代碼在ARC和MRC環(huán)境下都適用寥粹?
答:可以使用條件編譯來判斷當(dāng)前項(xiàng)目環(huán)境是ARC還是MRC
02 問:條件編譯的代碼呢变过,么么噠?
答:條件編譯
if
__has_feature(objc_arc)
如果是ARC涝涤,那么就執(zhí)行這里的代碼1
else
如果不是ARC媚狰,那么就執(zhí)行代理的代碼2
endif
03 問:在項(xiàng)目里面往往需要實(shí)現(xiàn)很多的單例,比如下載阔拳、網(wǎng)絡(luò)請(qǐng)求崭孤、音樂播放等等,弱弱的問一句單例可以用繼承嗎糊肠?
答:?jiǎn)卫遣豢梢杂美^承的辨宠,如果想寫一次四處使用,那么推薦親使用帶參數(shù)的宏定義啦货裹!
04 問:宏定義怎么弄嗤形?
答:這個(gè)嘛~~回頭看一眼我的代碼咯,親弧圆。
(2)使用帶參數(shù)的宏完成通用版單例模式代碼
01 注意條件編譯的代碼不能包含在宏定義里面
02 宏定義的代碼只需要寫一次就好赋兵,之后直接拖到項(xiàng)目中用就OK