單例創建的第一種寫法:多線程 dispatch_once
一般寫法:
#import <Foundation/Foundation.h> @interface FNWaterMarkHelper : NSObject + (instancetype)sharedWaterMark; @end
#import "FNUserInfoManager.h" static FNWaterMarkHelper *_showWaterMark = nil; + (instancetype)sharedWaterMark{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _showWaterMark = [[self alloc] init]; }); return _showWaterMark; } @end
輸出測試:
#import <Foundation/Foundation.h> #import "Downloader.h" int main(int argc, const char * argv[]) { @autoreleasepool { FNWaterMarkHelper *obj1 = [FNWaterMarkHelper sharedWaterMark]; NSLog(@"obj1 = %@", obj1); FNWaterMarkHelper *obj2 = [FNWaterMarkHelper sharedWaterMark]; NSLog(@"obj2 = %@", obj2); FNWaterMarkHelper *obj3 = [[FNWaterMarkHelper alloc] init]; NSLog(@"obj3 = %@", obj3); } return 0; }
結果:
obj1 = <Downloader: 0x78f25be0> obj2 = <Downloader: 0x78f25be0> obj3 = <Downloader: 0x79225e00>
解釋:
可以看到,當我們調用 sharedWaterMark 方法時獲取到的對象是相同的,但是當我們通過alloc和init來構造對象的時候,得到的對象卻是不一樣的。
那么問題就來了,我們通過不同的途徑得到不同的對象,顯然是不行的。我們必須要確保對象的唯一性,所以我們就需要封鎖用戶通過alloc和init以及copy來構造對象這條道路。
創建對象分為申請內存(alloc)、初始化(init)這兩個步驟,我們要確保對象的唯一性,因此在第一步這個階段我們就要攔截它。當我們調用alloc方法時,OC內部會調用allocWithZone這個方法來申請內存,我們覆寫這個方法,然后在這個方法中調用 sharedWaterMark 方法返回單例對象,這樣就可以達到我們的目的。拷貝對象也是同樣的原理,覆寫copyWithZone方法,然后在這個方法中調用sharedDownloader方法返回單例對象。
改進:
#import "FNUserInfoManager.h" static FNWaterMarkHelper *_showWaterMark = nil; + (instancetype)sharedWaterMark{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _showWaterMark = [[super allocWithZone:NULL] init]; }); return _showWaterMark; } + (id)allocWithZone:(struct _NSZone *)zone { return [FNWaterMarkHelper sharedWaterMark]; } - (id)copyWithZone:(struct _NSZone *)zone { return [FNWaterMarkHelper sharedWaterMark]; } @end
輸出結果:
obj1 = <Downloader: 0x7ca42e70> obj2 = <Downloader: 0x7ca42e70> obj3 = <Downloader: 0x7ca42e70>
搞定!
單例創建的第二種方法構造函數,不常用:
static PublicUtils *DefaultManager = nil; +(instancetype)sharedUtils { if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init]; return DefaultManager; }