書寫一個嚴謹的單例

前言

什麼是單例?數組

一個類只容許有一個實例,在整個程序中須要屢次使用,共享同一份資源的時候,就能夠建立單例,通常封裝成工具類使用,蘋果封裝成單例經常使用的有 UIApplication,NSUserDefaults,NSNotificationCenter,NSFIleManager等等。工具

單例的實現

我之前的寫法 (不嚴謹的寫法)post

// Singleton.h

@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@end

//  Singleton.m
static Singleton *_instance = nil;
@implementation Singleton
+(instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[Singleton alloc] init];
    });
    return _instance;
}
@end

這不算真正意義上的單例,不能保證全局的惟一性,由於類方法和 [ [Class alloc] init ] 方法建立的單例內存地址可能不同,全部不嚴謹。spa

下面舉個栗子🌰code

Singleton *singOne = [Singleton sharedInstance];
Singleton *singTwo = [Singleton sharedInstance];
Singleton *singThere = [Singleton new];
Singleton *singFour = [[Singleton alloc] init];
NSLog(@"singOne:-> %@",singOne);
NSLog(@"singTwo:-> %@",singTwo);
NSLog(@"singThere:-> %@",singThere);
NSLog(@"singFour:-> %@",singFour);

這時我看查看一下控制檯輸出的信息對象

2018-02-07 10:46:25.024988+0800 Markdown[2056:67507] singOne:-> <Singleton: 0x604000001a00>
2018-02-07 10:46:25.025222+0800 Markdown[2056:67507] singTwo:-> <Singleton: 0x604000001a00>
2018-02-07 10:46:25.025372+0800 Markdown[2056:67507] singThere:-> <Singleton: 0x6040000019a0>
2018-02-07 10:46:25.025646+0800 Markdown[2056:67507] singFour:-> <Singleton: 0x604000001a10>

結果很明顯, [Singleton sharedInstance] 類方法建立出來單例singOnesingTwo 的內純地址是同樣的,說明單例建立對了,可是看到newalloc init 建立的singTheresingFour 的內存地址不同,同時也與singOnesingTwo的地址不同.內存

下面怎麼聊聊怎麼解決這個問題

百度一下還真有這類的帖子和博客,咱們主要的問題就是保證單例的惟一性,避免不當心用實例方法建立單例,全部應該保證alloc initnewcopy 方法建立的單例的的惟一性。資源

在建立對象的時候主要分這麼兩步 alloc (申請內存)init(初始化)開發

  1. 咱們在第一步alloc的會後就要對其進行攔截。當咱們去調用alloc的時候,OC內部會調用allocWithZone這個方法去申請內存,咱們去覆寫這個方法,而後在這個方法中調用以前的類方法,返單例對象,這樣就能達到咱們的目的了。
  2. 拷貝對象也是同樣的,覆寫copyWithZone方法,而後在方法中去調用類方法,返回單例對象。(在覆寫copyWithZone方法以前別忘記了簽署NSCopying協議)get

  3. 參考:書寫一個嚴謹的單例

下面修改一下 Singleton.m
參考:iOS-單例模式簡單使用

// Singleton.m

static Singleton *_instance = nil;
@implementation Singleton
+(instancetype)sharedInstance
{
    if (_instance == nil) {
        _instance = [[super alloc]init];
    }
    return _instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}

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

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

看一下控制檯輸出的結果:

2018-02-07 11:20:46.993693+0800 Markdown[2374:88765] singOne:-> <Singleton: 0x604000001990>
2018-02-07 11:20:46.993906+0800 Markdown[2374:88765] singTwo:-> <Singleton: 0x604000001990>
2018-02-07 11:20:46.994040+0800 Markdown[2374:88765] singThere:-> <Singleton: 0x604000001990>
2018-02-07 11:20:46.994146+0800 Markdown[2374:88765] singFour:-> <Singleton: 0x604000001990>

很明顯,改過 Singleton.mSingleton類方法的類方法sharedInstance 和 實例方法 alloc init 的方法建立的單例內存地址同樣了,是否是有點小喜悅。

你覺得這就完事了嗎? NO、NO、NO

哪一個單例裏面沒有幾個屬性,最少也得有一個吧,要否則我要這鐵(dan)棒(li)有何用。
這就加幾個屬性試試。

Singleton *singOne = [Singleton sharedInstance];
SingOne.array = @[@"1",@"2",@"3"];
Singleton *singTwo = [Singleton sharedInstance];
SingOne.array = @[@"4",@"5",@"6"];
Singleton *singThere = [Singleton new];
SingThere.array = @[@"7",@"8",@"9"];
Singleton *singFour = [[Singleton alloc] init];
SingFour.array = @[@"0",@"0",@"0"];
NSLog(@"  singOne:-> %@ , %p , %@ ",singOne,singOne.array,singOne.array);
NSLog(@"  singTwo:-> %@ , %p , %@ ",singTwo,singTwo.array,singTwo.array);
NSLog(@"singThere:-> %@ , %p , %@ ",singThere,singThere.array,singThere.array);
NSLog(@" singFour:-> %@ , %p , %@ ",singFour,singFour.array,singFour.array);

如今看看結果
array 屬性地址惟一,數組內容也惟一,知足標準。

2018-02-07 11:48:39.843225+0800 Markdown[2804:110175]   singOne:-> <Singleton: 0x60000003ec60> , 0x600000445250 , (
    0,
    0,
    0
)
2018-02-07 11:48:39.843439+0800 Markdown[2804:110175]   singTwo:-> <Singleton: 0x60000003ec60> , 0x600000445250 , (
    0,
    0,
    0
)
2018-02-07 11:48:39.843589+0800 Markdown[2804:110175] singThere:-> <Singleton: 0x60000003ec60> , 0x600000445250 , (
    0,
    0,
    0
)
2018-02-07 11:48:39.843713+0800 Markdown[2804:110175]  singFour:-> <Singleton: 0x60000003ec60> , 0x600000445250 , (
    0,
    0,
    0
)

聲明

感謝以上兩位博主的文章,借鑑作了一份單例的筆記,記錄開發中的問題並解決問題。

相關文章
相關標籤/搜索