我先列舉一個蘋果官方文檔中的寫法。html
static AccountManager *DefaultManager = nil; ios
+ (AccountManager *)defaultManager { 多線程
if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init]; 併發
return DefaultManager; app
} 函數
iOS4以後新增長的方法:
ui
+ (AccountManager *)sharedManager this
{ spa
static AccountManager *sharedAccountManagerInstance = nil; 線程
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
關於dispatch_once 函數官方文檔解釋以下:
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
predicate |
A pointer to a |
block |
The block object to execute once. |
This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block.
If called simultaneously from multiple threads, this function waits synchronously until the block has completed.
The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.
Executes a block object once and only once for the lifetime of an application.
翻譯以後:
1.參數 predicate是一個指向dispatch_once_t結構體的指針,用來判斷下面的block體是否執行完畢
2.參數block即要被執行的block
3.在應用中這個函數是用來初始化一個全局變量(單例)的。在用block初始化任何變量以前老是調用這個函數。若是這個函數被多個線程同時調用,那麼函數會併發執行,直到block被執行完畢。換句話,函數支持多線程。predicate指針必須指向一個存儲在全局或者靜態存儲區的變量,不然致使的結果不可預知。
4.在一個應用的生命週期這個block執行且只執行一次
那麼按照官方文檔要求咱們先建立一個AccountManager類看一下,.m文件以下
static AccountMangager *_instance; @implementation AccountMangager + (instancetype)shareManager { static dispatch_once_t predicate; dispatch_once(&predicate, ^{ _instance = [[self alloc] init]; }); return _instance; }
外界引用:
- (void)viewDidLoad { [super viewDidLoad]; AccountMangager *manager1 = [AccountMangager shareManager]; NSLog(@"manager1 = %@",manager1); AccountMangager *manager2 = [AccountMangager shareManager]; NSLog(@"manager2 = %@",manager2); AccountMangager *manager3 = [[AccountMangager alloc] init]; NSLog(@"manager3 = %@",manager3); }
打印:
2015-12-13 14:17:54.642 單例模式[49159:17491715] manager1 = <AccountMangager: 0x7fc398f2cdb0>
2015-12-13 14:17:54.642 單例模式[49159:17491715] manager2 = <AccountMangager: 0x7fc398f2cdb0>
2015-12-13 14:17:54.642 單例模式[49159:17491715] manager3 = <AccountMangager: 0x7fc398f29c10>
你們能夠看到當咱們調用shareManager方法時獲取到的對象是相同的,可是當咱們經過alloc和init來構造對象的時候,獲得的對象倒是不同的。
那麼問題就來了,咱們經過不一樣的途徑獲得不一樣的對象,顯然是不行的。咱們必需要確保對象的惟一性,因此咱們就須要封鎖用戶經過alloc和init以及copy來構造對象這條道路。
咱們知道,建立對象的步驟分爲申請內存(alloc)、初始化(init)這兩個步驟,咱們要確保對象的惟一性,所以在第一步這個階段咱們就要攔截它。當咱們調用alloc方法時,oc內部會調用allocWithZone這個方法來申請內存,咱們覆寫這個方法,而後在這個方法中調用shareInstance方法返回單例對象,這樣就能夠達到咱們的目的。拷貝對象也是一樣的原理,覆寫copyWithZone方法,而後在這個方法中調用shareInstance方法返回單例對象
+(instancetype)shareManager { static dispatch_once_t predicate; dispatch_once(&predicate, ^{ _instance = [[super allocWithZone:NULL]init ]; }); return _instance; } + (id)allocWithZone:(struct _NSZone *)zone { return [AccountMangager shareManager]; } - (id)copy{ return [AccountMangager shareManager]; }
再次運行打印:
2015-12-13 14:31:57.413 單例模式[49215:17499767] manager1 = <AccountMangager: 0x7fcf58e233a0>
2015-12-13 14:31:57.414 單例模式[49215:17499767] manager2 = <AccountMangager: 0x7fcf58e233a0>
2015-12-13 14:31:57.414 單例模式[49215:17499767] manager3 = <AccountMangager: 0x7fcf58e233a0>