單例模式 概念相關 單例模式 在程式運行過程,一個類只有一個實例 使用場合 在整個應用程式中,共用一份資源(這份資源只需要創建初始化1次) static static關鍵字會在聲明變數的時候分配記憶體,在程式運行期間只分配一次記憶體。之後再訪問時,實際都是在訪問原先分配的記憶體 如果使用static來修飾 ...
單例模式
概念相關
- 單例模式
- 在程式運行過程,一個類只有一個實例
- 使用場合
- 在整個應用程式中,共用一份資源(這份資源只需要創建初始化1次)
static
- static關鍵字會在聲明變數的時候分配記憶體,在程式運行期間只分配一次記憶體。之後再訪問時,實際都是在訪問原先分配的記憶體
- 如果使用static來修飾局部變數,那麼局部變數在代碼塊結束後將不會回收,下次使用保持上次使用後的值。
- 如果使用static來修飾全局變數,那麼表示該全局變數只在本文件中有效,外界無法使用extern來引用。static變數的作用域被限制在定義變數的當前文件中,其它文件是不能訪問的。
ARC實現單例
步驟
01 在類的內部提供一個static修飾的全局變數
02 提供一個類方法,方便外界訪問
03 重寫+allocWithZone方法,保證永遠都只為單例對象分配一次記憶體空間
04 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
源碼
static ZCTools *_instance;
+ (instancetype)shareTools
{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
代碼分析
//提供一個static修飾的全局變數,強引用著已經實例化的單例對象實例
static ZCTools *_instance;
//類方法,返回一個單例對象
+ (instancetype)shareTools
{
//註意:這裡建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self alloc]init];
}
//保證永遠只分配一次存儲空間
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代碼
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加鎖的方式,保證只分配一次存儲空間
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
/*
1. mutableCopy 創建一個新的可變對象,並初始化為原對象的值,新對象的引用計數為 1;
2. copy 返回一個不可變對象。分兩種情況:(1)若原對象是不可變對象,那麼返回原對象,並將其引用計數加 1 ;(2)若原對象是可變對象,那麼創建一個新的不可變對象,並初始化為原對象的值,新對象的引用計數為 1。
*/
//讓代碼更加的嚴謹
- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
MRC實現單例
步驟
01 在類的內部提供一個static修飾的全局變數
02 提供一個類方法,方便外界訪問
03 重寫+allocWithZone方法,保證永遠都只為單例對象分配一次記憶體空間
04 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
05 重寫release方法
06 重寫retain方法
07 建議在retainCount方法中返回一個最大值
配置MRC環境
01 註意ARC不是垃圾回收機制,是編譯器特性
02 配置MRC環境:build setting ->搜索automatic ref->修改為NO
源碼
static ZCTools *_instance;
+ (instancetype)shareTools
{
return [[self alloc]init];
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
- (oneway void)release
{
}
- (instancetype)retain
{
return _instance;
}
- (NSUInteger)retainCount
{
return MAXFLOAT;
}
代碼分析
//提供一個static修飾的全局變數,強引用著已經實例化的單例對象實例
static ZCTools *_instance;
//類方法,返回一個單例對象
+ (instancetype)shareTools
{
//註意:這裡建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self alloc]init];
}
//保證永遠只分配一次存儲空間
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代碼
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加鎖的方式,保證只分配一次存儲空間
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
//讓代碼更加的嚴謹
- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
//在MRC環境下,如果用戶retain了一次,那麼直接返回instance變數,不對引用計數器+1
//如果用戶release了一次,那麼什麼都不做,因為單例模式在整個程式運行過程中都擁有且只有一份,程式退出之後被釋放,所以不需要對引用計數器操作
- (oneway void)release
{
}
- (instancetype)retain
{
return _instance;
}
//慣用法,有經驗的程式員通過列印retainCount這個值可以猜到這是一個單例
- (NSUInteger)retainCount
{
return MAXFLOAT;
}
通用版本
- 可以考慮一份單例代碼在ARC和MRC環境下都適用
(使用條件編譯來判斷當前項目環境是ARC還是MRC)
#if __has_feature(objc_arc)
//如果是ARC,那麼就執行這裡的代碼1
#else
//如果不是ARC,那麼就執行代理的代碼2
#endif
- 在項目裡面往往需要實現很多的單例,比如下載、網路請求、音樂播放等等,單例是否可用繼承呢?
單例是不可繼承,想一勞永逸可使用帶參數的巨集定義
使用帶參數的巨集完成通用版單例模式代碼
- 註意條件編譯的代碼不能包含在巨集定義裡面
- 巨集定義的代碼只用寫一次,之後直接拖到項目中用就OK
#define SingleH(name) +(instancetype)share##name;
//ARC
#if __has_feature(objc_arc)
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}
#else
//MRC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
return MAXFLOAT;\
}
#endif