iOS-嚴(yán)謹(jǐn)?shù)膯卫O(shè)計(jì)模式

什么是單例模式

  • 單例模式就是要保證系統(tǒng)中一個(gè)類只有一個(gè)對(duì)象實(shí)例。無(wú)論用什么方法創(chuàng)建多少次,所得的對(duì)象都是同一個(gè)對(duì)象。

單例模式的應(yīng)用場(chǎng)景

  • 在iOS開發(fā)中,我們已經(jīng)遇到過很多單例模式的身影:
    • [UIApplication sharedApplication]、[NSUserDefaults standardUserDefaults]、[NSNotificationCenter defaultCenter] 等等。
    • 音樂播放器中用于播放音樂的播放器對(duì)象、一個(gè)APP中用于保存并且能夠隨時(shí)方便地獲取的用戶信息的對(duì)象 等等。

單例模式的關(guān)鍵

  1. 對(duì)象只創(chuàng)建一次

  2. 可供全局訪問

  3. 不會(huì)被釋放,直至程序結(jié)束

單例模式的分析與實(shí)現(xiàn)

  • 對(duì)象只創(chuàng)建一次:
    在iOS中我們創(chuàng)建一個(gè)對(duì)象一般用:alloc init 或 new,其中new方法內(nèi)部實(shí)際也是通過alloc init創(chuàng)建的,所以我們把注意力放在alloc init上。首先alloc方法是給對(duì)象分配內(nèi)存空間,然后init方法是對(duì)該對(duì)象進(jìn)行初始化,所以想要控制對(duì)象的創(chuàng)建關(guān)鍵是在alloc方法上,又由于alloc默認(rèn)是調(diào)用allocWithZone方法,所以我們應(yīng)該重寫allocWithZone方法來(lái)控制對(duì)象只能創(chuàng)建一次:
id instance; // 定義全局變量來(lái)保存單例對(duì)象,此處還不完善,后面還會(huì)提到
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    // 此處if判斷可以避免頻繁加鎖,只要對(duì)象已創(chuàng)建就直接返回,亦相當(dāng)于懶加載
    if (instance == nil) {
        // 方法一:互斥鎖方式
        @synchronized(self) {
            if (instance == nil) {
                instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
            }
        }
        // 方法二:GCD一次性代碼方式
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
        });
    }
    return instance;
}
  • 可供全局訪問:
    單例模式要提供一個(gè)類方法來(lái)獲取單例對(duì)象,例如:

    Tools *tools = [Tools sharedTools];
    UserTool *userTool = [UserTool defaultUserTool];
    

    實(shí)現(xiàn)如下:

    // 單例類方法 命名規(guī)則: shared + 類名 或 default + 類名
    + (instancetype)sharedTools {
        if (instance == nil) {
            instance = [self alloc] init]; // 最終還是調(diào)用allocWithZone方法
        }
        return instance;
    }
    
  • 不會(huì)被釋放,直至程序結(jié)束:
    在第一個(gè)關(guān)鍵點(diǎn)中,我們定義了一個(gè)全局變量 id instance;來(lái)保存我們創(chuàng)建的單例對(duì)象,但是有個(gè)弊端,如果在別的文件中(別的類)使用extern關(guān)鍵字來(lái)獲取這個(gè)對(duì)象是可以拿到的,并且可以把該對(duì)象銷毀,例如:

    extern id instance;
    instance = nil;
    

    這樣以來(lái),下次再獲取單例對(duì)象的時(shí)候發(fā)現(xiàn)為nil就會(huì)重新創(chuàng)建對(duì)象,即二次創(chuàng)建對(duì)象,亦即不為單例模式,為了防止單例對(duì)象的銷毀,我們應(yīng)該使用static修飾用于保存單例對(duì)象的變量,限制變量的作用域?yàn)榇宋募捎茫敲磩e的文件(別的類)就無(wú)法拿到這個(gè)對(duì)象,從而達(dá)到單例對(duì)象不會(huì)被釋放。
    即把id instance;改為static id instance;

嚴(yán)謹(jǐn)?shù)膯卫J?/h2>
  • 創(chuàng)建對(duì)象除了alloc init 和 new 以外,還可以通過copy 和 mutableCopy來(lái)創(chuàng)建對(duì)象,為了嚴(yán)謹(jǐn)起見,我們還需要控制這兩個(gè)方法的創(chuàng)建過程,即需要重寫copyWithZone和mutableCopyWithZone方法,重寫這兩個(gè)方法需要分別遵守NSCopying 和 NSMutableCopying協(xié)議。
    因?yàn)檫@兩個(gè)方法是對(duì)象方法,所以當(dāng)想要使用這兩個(gè)方法來(lái)創(chuàng)建新對(duì)象的時(shí)候,只能是用單例對(duì)象來(lái)調(diào)用此方法,即單例對(duì)象已經(jīng)創(chuàng)建了,所以我們只需要返回我們保存的單例對(duì)象即可,代碼如下:

    - (id)copyWithZone:(NSZone *)zone {
        return instance;
    }
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return instance;
    }
    

至此一個(gè)嚴(yán)謹(jǐn)?shù)膯卫O(shè)計(jì)模式已經(jīng)完成了,下面附上完整代碼:

#import "Tools.h"

@implementation Tools

static id instance;
+ (instancetype)sharedTools {
    if (instance == nil) {
        instance = [[self alloc] init];
    }
    return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    // 此處if判斷可以避免頻繁加鎖,只要對(duì)象已創(chuàng)建就直接返回,亦相當(dāng)于懶加載
    if (instance == nil) {
        // 方法一:互斥鎖方式
        @synchronized(self) {
            if (instance == nil) {
                instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
            }
        }
        // 方法二:GCD一次性代碼方式
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [super allocWithZone:zone]; // 用super去調(diào)用,避免死循環(huán)
        });        
    }
    return instance;
}
// 遵守NSCopying協(xié)議 
- (id)copyWithZone:(NSZone *)zone {
    return instance;
}
// 遵守NSMutableCopying協(xié)議
- (id)mutableCopyWithZone:(NSZone *)zone {
    return instance;
}
@end

參考文章

http://lib.csdn.net/article/ios/35938

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容