1. 程式人生 > >iOS之單例模式常見寫法

iOS之單例模式常見寫法

單例模式可能是設計模式中最簡單的形式了,這一模式的意圖就是使得類中的一個物件成為系統中的唯一例項。它提供了對類的物件所提供的資源的全域性訪問點。因此需要用一種只允許生成物件類的唯一例項的機制。下面讓我們來看下單例的作用:

  • 可以保證的程式執行過程,一個類只有一個示例,而且該例項易於供外界訪問
  • 從而方便地控制了例項個數,並節約系統資源。

單例模式的使用場合

  • 類只能有一個例項,並且必須從一個為人數值的訪問點對其訪問。
  • 這個唯一的例項只能通過子類化進行拓展,並且拓展的物件不會破壞客戶端程式碼。
在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

通過上面的演練我們基本上學習了一些基本的單例模式然後在Cocoa Touch框架中同樣存在著大量的單例模式讓我們來學習,比如UIApplicationUIAccelerometer、以及NSFileManager等等。