iOS-嚴謹的單例設計模式

什么是單例模式

  • 單例模式就是要保證系統中一個類只有一個對象實例。無論用什么方法創建多少次,所得的對象都是同一個對象。

單例模式的應用場景

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

單例模式的關鍵

  1. 對象只創建一次

  2. 可供全局訪問

  3. 不會被釋放,直至程序結束

單例模式的分析與實現

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

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

    實現如下:

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

    extern id instance;
    instance = nil;
    

    這樣以來,下次再獲取單例對象的時候發現為nil就會重新創建對象,即二次創建對象,亦即不為單例模式,為了防止單例對象的銷毀,我們應該使用static修飾用于保存單例對象的變量,限制變量的作用域為此文件可用,那么別的文件(別的類)就無法拿到這個對象,從而達到單例對象不會被釋放。
    即把id instance;改為static id instance;

嚴謹的單例模式

  • 創建對象除了alloc init 和 new 以外,還可以通過copy 和 mutableCopy來創建對象,為了嚴謹起見,我們還需要控制這兩個方法的創建過程,即需要重寫copyWithZone和mutableCopyWithZone方法,重寫這兩個方法需要分別遵守NSCopying 和 NSMutableCopying協議。
    因為這兩個方法是對象方法,所以當想要使用這兩個方法來創建新對象的時候,只能是用單例對象來調用此方法,即單例對象已經創建了,所以我們只需要返回我們保存的單例對象即可,代碼如下:

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

至此一個嚴謹的單例設計模式已經完成了,下面附上完整代碼:

#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判斷可以避免頻繁加鎖,只要對象已創建就直接返回,亦相當于懶加載
    if (instance == nil) {
        // 方法一:互斥鎖方式
        @synchronized(self) {
            if (instance == nil) {
                instance = [super allocWithZone:zone]; // 用super去調用,避免死循環
            }
        }
        // 方法二:GCD一次性代碼方式
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [super allocWithZone:zone]; // 用super去調用,避免死循環
        });        
    }
    return instance;
}
// 遵守NSCopying協議 
- (id)copyWithZone:(NSZone *)zone {
    return instance;
}
// 遵守NSMutableCopying協議
- (id)mutableCopyWithZone:(NSZone *)zone {
    return instance;
}
@end

參考文章

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容