iOS之單例模式常見寫法
阿新 • • 發佈:2019-01-29
單例模式可能是設計模式中最簡單的形式了,這一模式的意圖就是使得類中的一個物件成為系統中的唯一例項。它提供了對類的物件所提供的資源的全域性訪問點。因此需要用一種只允許生成物件類的唯一例項的機制。下面讓我們來看下單例的作用:
- 可以保證的程式執行過程,一個類只有一個示例,而且該例項易於供外界訪問
- 從而方便地控制了例項個數,並節約系統資源。
單例模式的使用場合
- 類只能有一個例項,並且必須從一個為人數值的訪問點對其訪問。
- 這個唯一的例項只能通過子類化進行拓展,並且拓展的物件不會破壞客戶端程式碼。
在Objective-C中方法都是公有的,而且OC的語言本身是動態型別的,因此所有類都可以相互發送對方的訊息。,並且Cocoa框架使用計數的記憶體管理方式來維護物件的記憶體中的生存期。
下面讓我們看一下OC當中的單例模式的寫法,首先單例模式在ARC\MRC環境下的寫法有所不同,需要編寫2套不同的程式碼
- 可以用巨集判斷是否為ARC環境
#if _has_feature(objc_arc) #else //MRC #endif
單例模式- ARC -方法一
- ARC中單例模式的實現
- 在 .m中保留一個全域性的static的例項
static id _instance;
//重寫allocWithZone:方法,在這裡建立唯一的例項(注意執行緒安全)
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized (self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
- 提供1個類方法讓外界訪問唯一的例項
+ (instancetype)sharedInstanceTool{
@synchronized(self){
if(_instance == nil){
_instance = [[self alloc] init];
}
}
return _instance;
}
-
實現copyWithZone:方法
-(id)copyWithZone:(struct _NSZone *)zone{ return _instance; }
我們在
sharedInstanceTool
,首先檢查類的唯一例項是否已經建立,如果就會建立例項並將其返回。而之所以呼叫super而不是self,是因為已經在self中過載了基本的物件分配的方法,需要借用父類的功能來幫助處理底層記憶體的分配。在
allocWithZone:(struct _NSZone*)zone
方法中,只是返回從sharedInstanceTool
方法返回的類例項。而同樣的在Cocoa框架中呼叫allocWithZone:(struct _NSZone*)zone
會分配記憶體,引用計數會設定為1,然後返回例項。同樣的重寫(id)copyWithZone:(struct _NSZone *)zone
方法,也是為了保證不會返回例項的副本,而是返回self.返回同一個例項。
方法二:
+(instancetype)sharedInstance {
static WMSingleton *singleton = nil;
if (! singleton) {
singleton = [[self alloc] initPrivate];
}
return singleton;
}
- (instancetype)init {
@throw [NSException exceptionWithName:@"這個是個單例"
reason:@"應該這樣呼叫 [WMSingleton sharedInstance]"
userInfo:nil];
return nil;
}
//實現自己真正的私有初始化方法
- (instancetype)initPrivate {
self = [super init];
return self;
}
上面這段程式碼中將singleton指標宣告為靜態變數。當某個定義了靜態變數的方法返回時,程式不會釋放相應的變數。####singleton變數的初始值是nil,當程式第一次執行sharedInstance方法時會建立一個物件,並將新建立的物件的地址賦值給singleton變數。當徐成再次執行sharedInstance方法時,無論多少次singleton變數仍然會指向最初那個建立的物件。因為指向物件的singleton變數是強引用的,並且程式永遠不會釋放該變數,所以singleton變數指向的物件也不會釋放。
執行緒安全。
上面的例項中我們通過@synchronized
來添加了一個互斥鎖,以此來保證執行緒安全。而現在我們開始嘗試用執行緒的方式來實現一個加單的單例。
static WMObject *_instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
從上面的程式碼我們可以看到,實現的思路基本上也是一致的我們在sharedInstanceTool
,首先檢查類的唯一例項是否已經建立,如果就會建立例項並將其返回。而略有不同的地方就是我們這次通過dispatch_once_t
來保證執行緒的安全性。至於dispatch_once_t
的用法這裡就一一贅述了,執行緒的相關教程都會有其相關的描述。
到了這裡一個簡單的單例模式基本實現完成了,那麼我們可以嘗試著把它封裝到一個巨集裡,然後方便其以後的呼叫
建立一個WMSingleton.h
// .h檔案
#define WMSingletonH(name) + (instancetype)shared##name;
// .m檔案
#define WMSingletonM(name) \
static id _instance; \
\
+ (instancetype)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; \
}
使用方法
//.h類
//引入這個巨集檔案
#import "WMSingleton.h"
@interface WMObject : NSObject
WMSingletonH(object)
@end
//.m類
@implementation WMObject
WMSingletonM(Car)
@end