一. ARC環(huán)境下的單例模式
-
單例模式的基本概念
- 單例, 顧名思義, 即在整個(gè)程序中, 某一個(gè)類只有唯一一個(gè)實(shí)例, 他貫穿整個(gè)應(yīng)用程序, 不會(huì)被創(chuàng)建第二次, 分配第二次內(nèi)存.
- 對(duì)于單例, 較多的都是應(yīng)用于一些工具類, 可以起到特定作用并且功能比較常用的類, 就可以制作為單例模式.
-
在ARC環(huán)境下的單例模式
-
在類的內(nèi)部提供一個(gè)static修飾的全局變量
- 使用static修飾的變量, 它的生命周期會(huì)延長為整個(gè)程序結(jié)束的時(shí)候才被銷毀
- 并且這個(gè)變量只會(huì)生成一份內(nèi)存, 只會(huì)初始化一次
-
提供一個(gè)類方法, 方便外界訪問
- 這一點(diǎn)建議遵循蘋果的命名習(xí)慣:
shareXXXX
- 這一點(diǎn)建議遵循蘋果的命名習(xí)慣:
重寫
+allocWithZone方法, 保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間為了嚴(yán)謹(jǐn), 可以重寫
-copyWithZone和-mutableCopyWithZone方法-
單例代碼有兩種不同的方式
- GCD的一次性代碼:
dispatch_once- 使用的比較多, 保證block中的代碼, 在整個(gè)程序中只運(yùn)行一次
- 由于單例對(duì)象是一個(gè)靜態(tài)變量, 程序結(jié)束才釋放, 所以使用一次性代碼初始化后, 他會(huì)一直存在, 并通過返回單例對(duì)象的方法隨時(shí)可以獲取
- GCD線程鎖:
@synchronized- 此方法常用于多線程, 并且這個(gè)單例對(duì)象會(huì)頻繁被子線程使用的時(shí)候調(diào)用
- 建議使用一次性代碼的單例創(chuàng)建放放
- GCD的一次性代碼:
-
代碼示例:
// 1. 創(chuàng)建靜態(tài)變量 static FHTool * _instance; // 2. 重寫allocWithZone方法,確保內(nèi)存值分配一次并且每次返回的都是同一個(gè)實(shí)例 //+ (instancetype)allocWithZone:(struct _NSZone *)zone { // // // 2.1 為了防止多個(gè)線程同時(shí)使用該方法,避免訪問同一塊內(nèi)存出現(xiàn)問題,因此要加上互斥鎖 // @synchronized(self) { // if (_instance == nil) { // _instance = [super allocWithZone:zone]; // } // } // return _instance; //} // 3. 使用GCD一次性代碼 + (instancetype)allocWithZone:(struct _NSZone *)zone { // 3.1 一次性代碼保證全局只執(zhí)行一次, 之后永遠(yuǎn)返回靜態(tài)變量_instance static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } + (instancetype)shareFHTool { // alloc方法會(huì)在底層自動(dòng)調(diào)用allocWithZone方法 return [[self alloc] init]; } - (id)copy { return _instance; } - (id)mutableCopy { return _instance; }
-
二. MRC環(huán)境下的單例模式
與ARC相比, 目前使用MRC的項(xiàng)目基本也就是一些大公司的老項(xiàng)目, 所以建議只作為拓展知識(shí)理解一下
-
MRC單例的步驟
- 在類的內(nèi)部提供一個(gè)static修飾的全局變量
- 提供一個(gè)類方法方便外界調(diào)用:
shareXXX - 重寫
+allocWithZone方法, 保證永遠(yuǎn)只分配一次內(nèi)存 - 謹(jǐn)慎起見, 可以重寫
-copyWithZone和-mutableCopyWithZone方法, 每次都返回單例對(duì)象 - 重寫
-release方法, 單例對(duì)象不能被釋放掉 - 重寫
-retain方法, 單例對(duì)象不需要被多次引用, 每次retain只返回單例對(duì)象即可 - 建議重寫
-retainCount方法, 隨意給一個(gè)最大值, 提醒外界這是一個(gè)單例方法
-
注意點(diǎn):
- 在這里可以使用一個(gè)條件編譯, 寫一份單例代碼, 可以同時(shí)供ARC和MRC一起使用
-
#if __has_feature(objc_arc) #else使用這個(gè)條件編譯- 如果當(dāng)前是ARC的話, 就不需要重寫
-retain等MRC方法 - 如果是MRC的話, 再通過條件編譯將
MRC內(nèi)存管理方法加入編譯
- 如果當(dāng)前是ARC的話, 就不需要重寫
-
代碼示例:
//1. 創(chuàng)建靜態(tài)變量 static FHTool *_instance; + (instancetype)shareFHTool { return [[FHTool alloc] init]; } // 2. 重寫實(shí)例化方法 + (instancetype)allocWithZone:(struct _NSZone *)zone { // @synchronized(self) { // if (_instance == nil) { // _instance = [super allocWithZone:zone]; // } // } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } - (id)copy { return _instance; } - (id)mutableCopy { return _instance; } #if __has_feature(objc_arc) #else // 重寫release,讓單例對(duì)象不能被release - (oneway void)release { } // 重寫retain,單例不需要修改引用計(jì)數(shù) - (instancetype)retain { return _instance; } // 查詢當(dāng)前引用計(jì)數(shù),返回最大值即可 - (NSUInteger)retainCount { return MAXFLOAT; } #endif
三. 將單例模式封裝為宏
可以使用宏, 來講單例模式的代碼封裝到一個(gè)PCH全局性宏文件中
但是不推薦這樣使用, 按照蘋果的建議, 在程序中應(yīng)該盡量少用PCH文件
對(duì)于一個(gè)程序來說, 單例并不會(huì)有很多, 因此沒有太大的必要給單例代碼定義一個(gè)全局宏
-
因此代碼僅作為了解
#ifdef __OBJC__ // H文件 #define SingleH(name) +(instancetype)share##name; #if __has_feature(objc_arc) // M文件,ARC #define SingleM(name) static id _instance;\ + (instancetype)share##name {\ return [[self alloc] init];\ }\ + (instancetype)alloc {\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [super alloc];\ });\ return _instance;\ } #else // M文件,MRC #define SingleM(name) static id _instance;\ + (instancetype)share##name {\ return [[self alloc] init];\ }\ + (instancetype)alloc {\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [super alloc];\ });\ return _instance;\ }\ - (id)copy {\ return _instance;\ }\ - (id)mutableCopy {\ return _instance;\ } #endif #endif