單例模式出現(xiàn)以后,關(guān)于它的爭執(zhí)就一直存在排惨。在開發(fā)項目中,有很多時候我們需要一個全局的對象碰凶,而且要保證全局有且僅有一份即可暮芭。沒錯鹿驼,單例在這個時候就是最佳的選擇,但是需要注意的是:在多線程的環(huán)境下也需要做好線程保護辕宏。其實系統(tǒng)已經(jīng)有很多單例存在畜晰,例如UIApplication、NSNotification瑞筐、NSFileManager等等就是很不錯的例子——我們總有時候需要用到單例模式凄鼻,不過寫起代碼來還是需要考量考量。
-
我們先來看一個最簡單的單例聚假,假設我們有一個testClass的類需要實現(xiàn)單例:
+ (id)sharedInstance { static testClass *sharedInstance = nil; if (!sharedInstance) { sharedInstance = [[self alloc] init]; } return sharedInstance; }
-
熟悉單例的童鞋一眼就能看出块蚌,這里根本沒有考慮線程安全的問題,需要加上線程鎖膘格。
+ (id)sharedInstance { static testClass *sharedInstance = nil; @synchronized(self) { if (!sharedInstance) { sharedInstance = [[self alloc] init]; } } return sharedInstance; }
-
這是很常見的寫法匈子。不過,在GCD推出后闯袒,有個dispatch_once方法虎敦,可以使單例的實現(xiàn)更加容易,dispatch_once的函數(shù)原型如下:
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
我們可以看到這個函數(shù)接收一個dispatch_once_t的參數(shù)政敢,還有一個塊參數(shù)其徙。對于一個給定的predicate 來說,該函數(shù)會保證相關(guān)的塊必定會執(zhí)行喷户,而且只執(zhí)行一次唾那,最重要的是——這個方法是完全線程安全的。需要 注意的是褪尝,對于只需要執(zhí)行一次的塊來說闹获,傳入的predicate必須是完全相同的,所以predicate常常會用 static或者global來修飾河哑。
+ (id)sharedInstance { static testClass *sharedInstance = nil; static dispatch_once_t once; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
我們知道避诽,創(chuàng)建對象的步驟分為申請內(nèi)存(alloc)、初始化(init)這兩個步驟璃谨,我們要確保對象的唯一性沙庐,因此在第一步這個階段我們就要攔截它。當我們調(diào)用alloc方法時佳吞,OC內(nèi)部會調(diào)用allocWithZone這個方法來申請內(nèi)存拱雏,我們重寫這個方法,然后在這個方法中調(diào)用shareInstance方法返回單例對象底扳,這樣就可以達到我們的目的铸抑。拷貝對象也是同樣的原理衷模,重寫copyWithZone方法鹊汛,然后在這個方法中調(diào)用shareInstance方法返回單例對象菇爪。
下面來看看兩組例子:
一般寫法:
#import <Foundation/Foundation.h>
@interface SingleClass : NSObject
+(instancetype) shareInstance ;
@end
#import "SingleClass.h"
@implementation SingleClass
static SingleClass *_sharedInstance = nil;
+(instancetype) shareInstance
{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init] ;
}) ;
return _sharedInstance ;
}
@end
具體使用,ViewController:
#import "ViewController.h"
#import "SingleClass.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"開始《《《");
SingleClass *obj1 = [SingleClass shareInstance] ;
NSLog(@"obj1 = %@.", obj1) ;
SingleClass *obj2 = [SingleClass shareInstance] ;
NSLog(@"obj2 = %@.", obj2) ;
SingleClass *obj3 = [[SingleClass alloc] init] ;
NSLog(@"obj3 = %@.", obj3) ;
NSLog(@"結(jié)束》》》");
}
@end
輸出結(jié)果為 :
2016-04-11 15:49:29.494 aotulayoutDemo[7267:202275] 開始《《《
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj1 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj2 = <SingleClass: 0x7f901142d660>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] obj3 = <SingleClass: 0x7f901160e3a0>.
2016-04-11 15:49:29.495 aotulayoutDemo[7267:202275] 結(jié)束》》》
在這里可以看到柒昏,當我們調(diào)用shareInstance方法時獲取到的對象是相同的凳宙,但是當我們通過alloc和init來構(gòu)造對象的時候,得到的對象卻是不一樣的职祷。我們通過不同的途徑得到不同的對象氏涩,顯然是不行的。我們必須要確保對象的唯一性有梆,所以我們就需要封鎖用戶通過alloc和init以及copy來構(gòu)造對象這條道路是尖。
下面來看看嚴謹?shù)膶懛ǎ?/p>
#import "Singleton.h"
@implementation Singleton
static Singleton* _instance = nil;
+(instancetype) shareInstance
{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
_instance = [[super allocWithZone:NULL] init] ;
}) ;
return _instance ;
}
+(id) allocWithZone:(struct _NSZone *)zone
{
return [Singleton shareInstance] ;
}
-(id) copyWithZone:(struct _NSZone *)zone
{
return [Singleton shareInstance] ;
}
@end
再看看效果如何,ViewController:
#import "ViewController.h"
#import "SingleClass.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"開始《《《");
SingleClass *obj1 = [SingleClass shareInstance] ;
NSLog(@"obj1 = %@.", obj1) ;
SingleClass *obj2 = [SingleClass shareInstance] ;
NSLog(@"obj2 = %@.", obj2) ;
SingleClass *obj3 = [[SingleClass alloc] init] ;
NSLog(@"obj3 = %@.", obj3) ;
SingleClass* obj4 = [[SingleClass alloc] init] ;
NSLog(@"obj4 = %@.", [obj4 copy]) ;
NSLog(@"結(jié)束》》》");
}
@end
輸出結(jié)果:
2016-04-11 15:56:27.261 aotulayoutDemo[7373:205889] 開始《《《
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj1 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.263 aotulayoutDemo[7373:205889] obj2 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj3 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] obj4 = <SingleClass: 0x7fd9e261d690>.
2016-04-11 15:56:27.264 aotulayoutDemo[7373:205889] 結(jié)束》》》
這里我們可以看到泥耀,獲取到的對象都是一樣的了饺汹。