重點 (一) : 單例模式

weixin_33912445發表於2016-08-29

單例模式的作用

可以保證在程式執行過程,一個類只有一個例項,而且該例項易於供外界訪問

從而方便地控制了例項個數,並節約系統資源

單例模式的使用場合

在整個應用程式中,共享一份資源(這份資源只需要建立初始化1次)

單例模式在ARC\MRC環境下的寫法有所不同,需要編寫2套不同的程式碼

可以用巨集判斷是否為ARC環境

if __has_feature(objc_arc)

// ARC

else

// MRC

endif

單例模式 - ARC

ARC中,單例模式的實現

在.m中保留一個全域性的static的例項

static id _instance;

重寫allocWithZone:方法,在這裡建立唯一的例項(注意執行緒安全)

(id)allocWithZone:(struct
_NSZone *)zone {

if (_instance == nil) { //

防止頻繁加鎖

    @synchronized(self) {

        if

(_instance== nil) { //
防止建立多次

_instance = [super allocWithZone:zone];

      }


  }

}

return _instance;

}

提供1個類方法讓外界訪問唯一的例項

(instancetype)sharedMusicTool {

if (_instance == nil) { //

防止頻繁加鎖

    @synchronized(self) {

        if

(_instance== nil) { //
防止建立多次

        _instance

= [[self alloc]
init];

      }


  }

}

return _instance;

}

實現copyWithZone:方法

(id)copyWithZone:(struct
_NSZone *)zone {

return _instance;

}

單例模式 – 非ARC

非ARC中(MRC),單例模式的實現(比ARC多了幾個步驟)

實現記憶體管理方法

(id)retain { return
self; }

(NSUInteger)retainCount {return 1; }

(oneway void)release {}

  • (id)autorelease { return
    self; }

************************筆記****************************


1.單例模式

  • 1.1 概念相關

(1)單例模式

在程式執行過程,一個類只有一個例項

(2)使用場合

在整個應用程式中,共享一份資源(這份資源只需要建立初始化1次)

  • 1.2
    ARC實現單例

(1)步驟

01 在類的內部提供一個static修飾的全域性變數

02 提供一個類方法,方便外界訪問

03 重寫+allocWithZone方法,保證永遠都只為單例物件分配一次記憶體空間

04 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法

(2)相關程式碼

提供一個static修飾的全域性變數,強引用著已經例項化的單例物件例項

static XMGTools *_instance;

類方法,返回一個單例物件

+(instancetype)shareTools

{

注意:這裡建議使用self,而不是直接使用類名Tools(考慮繼承)

return [[self
alloc]init];

}

保證永遠只分配一次儲存空間

+(instancetype)allocWithZone:(struct _NSZone *)zone

{

使用GCD中的一次性程式碼

static

dispatch_once_t onceToken;

dispatch_once(&onceToken,

^{

_instance

= [super allocWithZone:zone];

});

使用加鎖的方式,保證只分配一次儲存空間

@synchronized(self) {

if (_instance ==nil) {

  _instance = [super

allocWithZone:zone];

}

}

return _instance;

}

  1. mutableCopy 建立一個新的可變物件,並初始化為原物件的值,新物件的引用計數為1;

  2. copy 返回一個不可變物件。分兩種情況:(1)若原物件是不可變物件,那麼返回原物件,並將其引用計數加1;(2)若原物件是可變物件,那麼建立一個新的不可變物件,並初始化為原物件的值,新物件的引用計數為1。

讓程式碼更加的嚴謹

-(nonnull id)copyWithZone:(nullable
NSZone *)zone

{

return [[self class] allocWithZone:zone];

return _instance;

}

-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone

{

return _instance;

}

  • 1.3
    MRC實現單例

(1)實現步驟

01 在類的內部提供一個static修飾的全域性變數

02 提供一個類方法,方便外界訪問

03 重寫+allocWithZone方法,保證永遠都只為單例物件分配一次記憶體空間

04 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法

05 重寫release方法

06 重寫retain方法

07 建議在retainCount方法中返回一個最大值

(2)配置MRC環境知識

01 注意ARC不是垃圾回收機制,是編譯器特性

02 配置MRC環境:build
setting ->搜尋automatic ref->修改為NO

(3)相關程式碼

提供一個static修飾的全域性變數,強引用著已經例項化的單例物件例項

static XMGTools *_instance;

類方法,返回一個單例物件

+(instancetype)shareTools

{

注意:這裡建議使用self,而不是直接使用類名Tools(考慮繼承)

return [[self
alloc]init];

}

保證永遠只分配一次儲存空間

+(instancetype)allocWithZone:(struct _NSZone *)zone

{

使用GCD中的一次性程式碼

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

_instance = [super allocWithZone:zone];

});

使用加鎖的方式,保證只分配一次儲存空間

@synchronized(self) {

if (_instance ==nil) {

  _instance = [super

allocWithZone:zone];

}

}

return _instance;

}

讓程式碼更加的嚴謹

-(nonnull id)copyWithZone:(nullable
NSZone *)zone

{

return

[[self class] allocWithZone:zone];

return _instance;

}

-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone

{

return _instance;

}

在MRC環境下,如果使用者retain了一次,那麼直接返回instance變數,不對引用計數器+1

如果使用者release了一次,那麼什麼都不做,因為單例模式在整個程式執行過程中都擁有且只有一份,程式退出之後被釋放,所以不需要對引用計數器操作

-(oneway void)release

{

}

-(instancetype)retain

{

return _instance;

}

慣用法,有經驗的程式設計師通過列印retainCount這個值可以猜到這是一個單例

-(NSUInteger)retainCount

{

return MAXFLOAT;

}

  • 1.4 通用版本

(1)有意思的對話

01 問:寫一份單例程式碼在ARC和MRC環境下都適用?

答:可以使用條件編譯來判斷當前專案環境是ARC還是MRC

02 問:條件編譯的程式碼呢,麼麼噠?

答:條件編譯

if

__has_feature(objc_arc)

如果是ARC,那麼就執行這裡的程式碼1

else

如果不是ARC,那麼就執行代理的程式碼2

endif

03 問:在專案裡面往往需要實現很多的單例,比如下載、網路請求、音樂播放等等,弱弱的問一句單例可以用繼承嗎?

答:單例是不可以用繼承的,如果想寫一次四處使用,那麼推薦親使用帶引數的巨集定義啦!

04 問:巨集定義怎麼弄?

答:這個嘛~~回頭看一眼我的程式碼咯,親。

(2)使用帶引數的巨集完成通用版單例模式程式碼

01 注意條件編譯的程式碼不能包含在巨集定義裡面

02 巨集定義的程式碼只需要寫一次就好,之後直接拖到專案中用就OK

相關文章