單例模式出現(xiàn)以后,關(guān)于它的爭(zhēng)執(zhí)就一直存在。在開發(fā)項(xiàng)目中,有很多時(shí)候我們需要一個(gè)全局的對(duì)象,而且要保證全局有且僅有一份即可。沒錯(cuò),單例在這個(gè)時(shí)候就是最佳的選擇,但是需要注意的是:在多線程的環(huán)境下也需要做好線程保護(hù)。其實(shí)系統(tǒng)已經(jīng)有很多單例存在,例如UIApplication、NSNotification、NSFileManager等等就是很不錯(cuò)的例子——我們總有時(shí)候需要用到單例模式,不過寫起代碼來還是需要考量考量。
-
我們先來看一個(gè)最簡(jiǎn)單的單例,假設(shè)我們有一個(gè)testClass的類需要實(shí)現(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推出后,有個(gè)dispatch_once方法,可以使單例的實(shí)現(xiàn)更加容易,dispatch_once的函數(shù)原型如下:
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);我們可以看到這個(gè)函數(shù)接收一個(gè)dispatch_once_t的參數(shù),還有一個(gè)塊參數(shù)。對(duì)于一個(gè)給定的predicate 來說,該函數(shù)會(huì)保證相關(guān)的塊必定會(huì)執(zhí)行,而且只執(zhí)行一次,最重要的是——這個(gè)方法是完全線程安全的。需要 注意的是,對(duì)于只需要執(zhí)行一次的塊來說,傳入的predicate必須是完全相同的,所以predicate常常會(huì)用 static或者global來修飾。
+ (id)sharedInstance { static testClass *sharedInstance = nil; static dispatch_once_t once; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } 我們知道,創(chuàng)建對(duì)象的步驟分為申請(qǐng)內(nèi)存(alloc)、初始化(init)這兩個(gè)步驟,我們要確保對(duì)象的唯一性,因此在第一步這個(gè)階段我們就要攔截它。當(dāng)我們調(diào)用alloc方法時(shí),OC內(nèi)部會(huì)調(diào)用allocWithZone這個(gè)方法來申請(qǐng)內(nèi)存,我們重寫這個(gè)方法,然后在這個(gè)方法中調(diào)用shareInstance方法返回單例對(duì)象,這樣就可以達(dá)到我們的目的??截悓?duì)象也是同樣的原理,重寫copyWithZone方法,然后在這個(gè)方法中調(diào)用shareInstance方法返回單例對(duì)象。
下面來看看兩組例子:
一般寫法:
#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é)束》》》
在這里可以看到,當(dāng)我們調(diào)用shareInstance方法時(shí)獲取到的對(duì)象是相同的,但是當(dāng)我們通過alloc和init來構(gòu)造對(duì)象的時(shí)候,得到的對(duì)象卻是不一樣的。我們通過不同的途徑得到不同的對(duì)象,顯然是不行的。我們必須要確保對(duì)象的唯一性,所以我們就需要封鎖用戶通過alloc和init以及copy來構(gòu)造對(duì)象這條道路。
下面來看看嚴(yán)謹(jǐn)?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é)束》》》
這里我們可以看到,獲取到的對(duì)象都是一樣的了。