單例的意思從字面上就可以略知一二,所謂單例就是確保在程序運行過程中只創建一個對象實例。可以用於需要被多次廣泛或者說多次使用的資源中,比如我們常見的網絡請求類、工具類以及其它管理類等。比如我iOS開發中常見的系統單例[UIApplication sharedApplication]、[NSUserDefaults standardUserDefaults]等。在iOS開發中,單例模式是非常有用的一種設計模式。如下圖,是一個簡單的例模式的UML類圖。
一、使用單例模式的作用
它可以保證某個類在程序運行過程中最多只有一個實例,也就是對象實例只占用一份內存資源。
二、單例模式的三個要點:
1. 該類有且只有一個實例;
2. 該類必須能夠自行創建這個實例;
3. 該類必須能夠自行向整個系統提供這個實例。
三、為什么需要使用單例
1.節省內存開銷。如果某個對象需要被多個其它對象使用,那可以考慮使用單例,因為這樣該類只使用一份內存資源。
2.使用單例,可以確保其它類只獲取類的一份數據(變量值)。
四、單例模式優缺點
優點
1、提供了對唯一實例的受控訪問。
2、由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷毀的對象單例模式無疑可以提高系統的性能。
3.因為單例模式的類控制了實例化的過程,所以類可以更加靈活修改實例化過程。
缺點
1、由於單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
2、單例類的職責過重,在一定程度上違背了“單一職責原則”。
五、單例的實現
基本步驟:
(1) 為單例對象創建一個靜態實例,可以寫成全局的,也可以在類方法里面實現,並初始化為nil;
(2) 實現一個實例構造方法,檢查上面聲明的靜態實例是否為nil,如果是,則創建並返回一個本類的實例;
(3) 重寫allocWithZone方法,用來保證其他人直接使用alloc和init試圖獲得一個新實力的時候不產生一個新實例;
(4) 適當實現copyWithZone,mutableCopyWithZone,非arc下還需要實現release和autorelease方法。
下面我們來新建一個Singleton類,在Singleton.h中實現如下:
@interface Singleton : NSObject + (Singleton *) sharedInstance; @end
在Singleton.m添加如下內容:
@implementation Singleton static Singleton * sharedSingleton = nil; + (Singleton *) sharedInstance { if (sharedSingleton == nil) { sharedSingleton = [[Singleton alloc] init]; } return sharedSingleton; } @end
這樣就創建一個簡單的單例模式,實際上有一部分程序員也是這樣實現的,但實際上這是一個不“嚴格”版本,在實際中使用,如果我們使用alloc,copy等方法創建對象時,依然會創建新的實例。而且如果多線程同時訪問時候也會創建多個實例,因此這樣做是非線程安全的。
下面我對Singleton.m的進行改進:
@implementation Singleton static id sharedSingleton = nil; + (id)allocWithZone:(struct _NSZone *)zone { if (_instace == nil) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedSingleton = [super allocWithZone:zone]; }); } return sharedSingleton; } - (id)init { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedSingleton = [super init]; }); return sharedSingleton; } + (instancetype)sharedInstance
{
return [[self alloc] init];
}
+ (id)copyWithZone:(struct _NSZone *)zone
{
return sharedSingleton;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone
{
return sharedSingleton;
}
@end
這是一個比較完整的單例實現,這樣在我們init,alloc,copy,mutableCopy時,都能保證只創建唯一單例。
六、把單例抽取成宏
有時候我們一個程序會有多個單例,如果我們每個都去是有上面的代碼,未免有些麻煩,因此我們可以講上面的代碼定義為宏,並保存在一個新文件中,然后就可以再任何需要實現單例模式的類中簡單添加一兩行代碼就可以了。
1.創建一個頭文件Singleton.h,並添加如下代碼:
// 幫助實現單例設計模式 // .h文件的實現 #define SingletonH(methodName) + (instancetype)shared##methodName; // .m文件的實現 #if __has_feature(objc_arc) // 是ARC #define SingletonM(methodName) \ static id _instace = nil; \ + (id)allocWithZone:(struct _NSZone *)zone \ { \ if (_instace == nil) { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instace = [super allocWithZone:zone]; \ }); \ } \ return _instace; \ } \ \ - (id)init \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instace = [super init]; \ }); \ return _instace; \ } \ \ + (instancetype)shared##methodName \ { \ return [[self alloc] init]; \ } \ + (id)copyWithZone:(struct _NSZone *)zone \ { \ return _instace; \ } \ \ + (id)mutableCopyWithZone:(struct _NSZone *)zone \ { \ return _instace; \ } #else // 不是ARC #define SingletonM(methodName) \ static id _instace = nil; \ + (id)allocWithZone:(struct _NSZone *)zone \ { \ if (_instace == nil) { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instace = [super allocWithZone:zone]; \ }); \ } \ return _instace; \ } \ \ - (id)init \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _instace = [super init]; \ }); \ return _instace; \ } \ \ + (instancetype)shared##methodName \ { \ return [[self alloc] init]; \ } \ \ - (oneway void)release \ { \ \ } \ \ - (id)retain \ { \ return self; \ } \ \ - (NSUInteger)retainCount \ { \ return 1; \ } \ + (id)copyWithZone:(struct _NSZone *)zone \ { \ return _instace; \ } \ \ + (id)mutableCopyWithZone:(struct _NSZone *)zone \ { \ return _instace; \ }
2.這樣我們在需要實現單例模式的類中輕易就可以實現單例模式了,假設有個工具類MJSoundTool。因此就可以這么做實現單例模式。
頭文件MJSoundTool.h
#import <Foundation/Foundation.h> #import "Singleton.h" @interface MJSoundTool : NSObject //+ (instancetype)sharedSoundTool; SingletonH(SoundTool) @end
實現文件MJSoundTool.m
#import "MJSoundTool.h" @implementation MJSoundTool SingletonM(SoundTool) @end
是不是很方便呢,我們以后需要設計單例的時候就可以直接使用該單例宏文件了。下載地址:單例宏Singleton.h。好了,單例模式就介紹到這里。😊