IOS開發模式-單例模式
正文
其實小編在接觸了蘋果的新的開發語言swift之后,深刻覺得單例模式在swift里面體現的淋漓盡致,不過在OC里面確實會有一些麻煩。簡單對比一下OC的單例模式和swift的單例模式吧。拿小編之前的一個程序開刀。
struct DeviceData { static let height = UIScreen.mainScreen().bounds.height static let width = UIScreen.mainScreen().bounds.width static let bound = UIScreen.mainScreen().bounds }
再來看一下oc版本的單例
@implementation HMMusicTool static id _instance; /** * alloc方法內部會調用這個方法 */ + (id)allocWithZone:(struct _NSZone *)zone { if (_instance == nil) { // 防止頻繁加鎖 @synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [super allocWithZone:zone]; } } } return _instance; } + (instancetype)sharedMusicTool { if (_instance == nil) { // 防止頻繁加鎖 @synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [[self alloc] init]; } } } return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } @end
這時候swift的優勢可見一斑吧。不過小編還是會辯證的去看待問題的,swift還是存在一些兼容性的問題的。這次小編先拿oc開刀。底層實現都是一樣的。
簡單介紹單例模式 防止大家百度,小編直接搬來了爛大街的介紹
單例模式的意思就是只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。這個類稱為單例類。
1.單例模式的要點:
顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
2.單例模式的優點:
1.實例控制:Singleton 會阻止其他對象實例化其自己的 Singleton 對象的副本,從而確保所有對象都訪問唯一實例。2.靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程
那么如何優雅的實現oc的單例模式呢。
ARC + 互斥鎖版本
第一步。 為單例對象實現一個靜態實例,并初始化,然后設置成nil
第二步。檢查靜態實例的值是否為nil
第三步。重寫 allocWithZone, copyWithZone 。
第四步。加鎖
代碼如下:
+ (id)allocWithZone:(struct _NSZone *)zone { if (_instance == nil) { // 防止頻繁加鎖 @synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [super allocWithZone:zone]; } } } return _instance; } + (instancetype)sharedMusicTool { if (_instance == nil) { // 防止頻繁加鎖 @synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [[self alloc] init]; } } } return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } @end
調用
HMMusicTool *tool = [[HMMusicTool alloc] init]; HMMusicTool *tool2 = [[HMMusicTool alloc] init]; HMMusicTool *tool3 = [HMMusicTool sharedMusicTool]; HMMusicTool *tool4 = [HMMusicTool sharedMusicTool];
tool。tool2.tool3.tool4的地址是一致的。簡單分析一下上面的程序。類HMMusicTool在alloc的時候會調用 allocWithZone方法。所以重寫 allocWithZone方法。
@synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [super allocWithZone:zone]; } }
判斷是否創建多次。然后加一個互斥鎖,防止在多線程中創建多個對象,返回對象_instance.
除了通過alloc創建之外,還可以通過類方法創建,典型的創建方法例如 [ UIApplication sharedApplication ]然后我們創建一個類方法。
+ (instancetype)sharedMusicTool { if (_instance == nil) { // 防止頻繁加鎖 @synchronized(self) { if (_instance == nil) { // 防止創建多次 _instance = [[self alloc] init];//如果第一次使用。調用allocWithZone。 } } } return _instance; }
當然除了以上調用方式外。還有可能通過copy實現對象的復制。還需要重寫copy方法。這時候需要遵循NSCoping協議。
- (id)copyWithZone:(NSZone *)zone { return _instance; }
當調用copyWithZone的時候,往往已經實例化了一個變量。這時候_instance是存在的。所以直接返回即可。
為什么_instance要加上static呢。
_instance作為全局變量。如果不加上static。則在程序的其他文件里面可以通過extern訪問。對_instance的值進行更改,這一點是非常危險的。但是加上static。_instance只能在當前文件訪問。其他文件訪問出錯。
ARC + GCD版本
相對互斥鎖版本沒有相差太多,只是使用了GCD的一個方法。
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ <#code to be executed once#> });
閉包里面的代碼只能被執行一次。所以更改后的程序為
// 用來保存唯一的單例對象 static id _instace; + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [super allocWithZone:zone]; }); return _instace; } + (instancetype)sharedDataTool { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [[self alloc] init]; }); return _instace; } - (id)copyWithZone:(NSZone *)zone { return _instace; } @end
非ARC版本 單例模式
非ARC版本需要手動管理內存,所以單例模式需要防止單例被釋放,這時候需要重寫幾個方法
- (oneway void)release { } - (id)retain { return self; } - (NSUInteger)retainCount { return 1;} - (id)autorelease { return self;}
這樣子就防止了單例被釋放,保持單例的計數為1。全部代碼為GCD+非ARC
static id _instace; + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [super allocWithZone:zone]; }); return _instace; } + (instancetype)sharedDataTool { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [[self alloc] init]; }); return _instace; } - (id)copyWithZone:(NSZone *)zone { return _instace; } - (oneway void)release { } - (id)retain { return self; } - (NSUInteger)retainCount { return 1;} - (id)autorelease { return self;}
總結
單例在程序開發當中,有著舉足輕重的地位。當有多個單例對象需要創建的時候,可以考慮把單例抽出來作為宏,不過相對來說調試來說確實會比較坑。不過一切為了簡單嘛。如下
// .h文件 #define HMSingletonH(name) + (instancetype)shared##name; // .m文件 #define HMSingletonM(name) \ static id _instance; \ \ + (id)allocWithZone:(struct _NSZone *)zone \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instance = [super allocWithZone:zone]; \ }); \ return _instance; \ } \ \ + (instancetype)shared##name \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instance = [[self alloc] init]; \ }); \ return _instance; \ } \ \ - (id)copyWithZone:(NSZone *)zone \ { \ return _instance; \ }