就是保證某個類建立出來的對象從始到終只有一個的一種方案git
首先將咱們的環境設置爲非ARC環境,即MRC,如圖 github
在MRC模式下,咱們得本身手動釋放資源,因此得重寫一些與資源建立與釋放相關的方法,以保證單例對象的惟一。設計模式
新建一個繼承於NSObject的類 LXFFileTool,我直接上代碼,並寫上註釋 LXFFileTool.h安全
@interface LXFFileTool : NSObject
+ (instancetype)sharedFileTool;
@end
複製代碼
LXFFileTool.mbash
#import "LXFFileTool.h"
@implementation LXFFileTool
static LXFFileTool *_fileTools = nil;
/** * alloc方法內部會調用allocWithZone: * @param zone 系統分配給app的內存 */
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_fileTools == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 安全(這個代碼只會被調用一次)
_fileTools = [super allocWithZone:zone];
});
}
return _fileTools;
}
- (oneway void)release {
// 在allocWithZone中使用了GCD令建立對象的代碼只執行一次,若是_fileTools被釋放則沒法再建立
// 重寫release方法,防止_fileTools被釋放
}
// 重寫retain方法
- (instancetype)retain {
return self;
}
// 重寫retainCount鎖定引用計數
- (NSUInteger)retainCount {
return 1;
}
// 重寫init方法,防止單例所擁有的屬性值被重置
// 讓初始化的方法只能執行一次,天然屬性值就沒有機會被重置
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super init]; // init會先調用alloc方法
});
return _fileTools;
}
// 仿造系統的單例建立方式,提供類方法
+ (instancetype)sharedFileTool {
// 因爲咱們已經重寫了init方法保證了單例對象的惟一了,因此這裏直接調用init方法便可。
return [[self alloc] init];
}
@end
複製代碼
MRC下就是這樣,咱們的目的就是隻能建立和初始化一次對象,不給機會釋放,也不給機會從新初始化,從而保證了該對象的惟一。微信
那如今來看看ARC下是如何實現單例的吧。其實ARC下與MRC的區別就是ARC下咱們不用本身再手動去釋放資源了,從而使代碼上大同小異,以下所示。app
#import "LXFFileTool.h"
@implementation LXFFileTool
static LXFFileTool *_fileTools = nil;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (_fileTools == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super allocWithZone:zone];
});
}
return _fileTools;
}
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_fileTools = [super init];
});
return _fileTools;
}
+ (instancetype)sharedFileTool {
return [[self alloc] init];
}
複製代碼
如今咱們已經知道了ARC與MRC下分別是如何建立單例的了,可是若是咱們一個項目裏須要多個單例,那咱們只能把代碼複製粘貼再改改就完事嗎?這未免也太麻煩了吧。那咱們能不能作到快速且方便的建立單例對象呢?能夠的,利用宏ui
首先先說下一些關於宏的知識吧spa
那如今來討論下一些疑惑吧,你說宏只能單行,但是建立單例的代碼但是有不少行呀!還有咱們如何作到自定義類方法名(就是 sharedXXX )?好,咱們來介紹下宏下的兩個特殊符號設計
做用 | |
---|---|
\ | 用來轉譯換行符,即屏蔽換行符 |
## | 將兩個相鄰的標記(token)鏈接爲一個單獨的標記 |
想了解其它關於宏的預處理命令能夠自行百度參考"C語言的預處理命令"
簡單來講,\用於取消換行,##用來鏈接,而咱們就用##來實現自定義類方法名
建立一個頭文件Singleton.h用來存放宏定義 先來看看定義.h中 sharedXXX 是如何經過宏來定義的
// .h文件的實現
#define SingletonH(methodName) + (instancetype)shared##methodName;
複製代碼
如今回到LXFFileTool.h中,直接一行定義sharedFileTool這個類方法
#import "Singleton.h"
@interface LXFFileTool : NSObject
SingletonH(FileTool)
@end
複製代碼
咱們只須要將方法名FileTool傳入SingletonH()中就能夠拼接爲sharedFileTool
那如今再來看看定義.m中建立單例的方式,以ARC爲例
#define SingletonM(methodName) \
static id _instance = nil; \
+ (instancetype)allocWithZone:(struct _NSZone *)zAone { \
if (_instance == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
} \
return _instance; \
} \
\
- (instancetype)init { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super init]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##methodName { \
return [[self alloc] init]; \
}
複製代碼
在每個行後面加上(反斜槓)取消換行,使用##來拼接傳入的方法名,但還有一點須要注意:最後一行不能加反斜槓
回到LXFFileTool.m中,一行實現建立單例
#import "LXFFileTool.h"
@implementation LXFFileTool
SingletonM(FileTool)
@end
複製代碼
好,如今還有一個問題,就是若是個人項目中有個別文件是須要MRC環境的,那我該怎麼辦才能讓建立單例也是如此簡單呢?很簡單,加個判斷就行了,大體判斷以下,詳情看文章最後附上的Demo
#if __has_feature(objc_arc) // ARC
// 寫上ARC下的定義代碼
#else // 非ARC
// 寫上MRC下的定義代碼
#endif
複製代碼
好了,如今用起來是否是方便多了?咱們只要建立一個類,而後在.h文件中寫SingletonH(XXX),再在.m文件中寫SingletonM(XXX)就能夠實現單例了~
順便提下如何在MRC下指定某個類文件使用的環境爲ARC
如圖,能夠在 Build Phases -> Compile Sources 中雙擊某個須要ARC環境的類文件,而後寫上-fobjc-arc
複製代碼
若是是指定MRC,則寫上
-fno-objc-arc
複製代碼
最後,附上Demo: LXFSingleton