單例模式怎麼定義的,可能在不一樣的語言,不一樣的書中不徹底同樣,可是概況開來都應該是:一個類有且僅有一個實例,而且自行實例化向整個系統提供。
git
所以,首先你可能須要肯定你是真的須要一個單例類,仍是說僅僅是須要一個方便調用的實例化方法。若是你是真的須要一個單例類,那麼你就應該確保這個單例類,有且僅有一個實例(無論怎麼操做都只能獲取到這個實例)。github
最近看到一些github上的單例使用,別人的用法,有一些思考,而後寫demo測試了下,就這個簡單的單例也有一些坑呢,但願能給他人一些提醒。bash
#Objective-C中的單例ide
咱們一般在OC中實現一個單例方法都是這樣:測試
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
複製代碼
但是這樣就能夠了麼?我作了以下測試:ui
HLTestObject *objct1 = [HLTestObject sharedInstance];
NSLog(@"%@",objct1);
HLTestObject *objc2 = [[HLTestObject alloc] init];
NSLog(@"%@",objc2);
HLTestObject *objc3 = [HLTestObject new];
NSLog(@"%@",objc3);
複製代碼
看到這個測試,你想到打印結果了麼?結果是這樣的:this
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <HLTestObject: 0x7fcf39515510>
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <HLTestObject: 0x7fcf395c4b70>
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <HLTestObject: 0x7fcf395c6890>
複製代碼
很明顯,經過三種方式建立出來的是不一樣的實例對象,這就違背了單例類有且僅有一個實例
的定義。atom
爲了防止別人不當心利用alloc/init方式建立示例,也爲了防止別人故意爲之,咱們要保證無論用什麼方式建立都只能是同一個實例對象,這就得重寫另外一個方法,實現以下:spa
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
複製代碼
再次用上面的測試代碼,結果是這樣的:code
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <HLTestObject: 0x7f88b9488ac0>
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <HLTestObject: 0x7f88b9488ac0>
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <HLTestObject: 0x7f88b9488ac0>
複製代碼
好像用不一樣的構造方法,獲取的都是同一個對象,你覺得這樣就完了?還早着呢! 通常咱們的類裏確定都會有一些屬性,而後我就添加了兩個property:
@property (assign, nonatomic) int height;
@property (strong, nonatomic) NSObject *object;
@property (strong, nonatomic) NSMutableArray *arrayM;
複製代碼
而一些對象類的初始化,或者基礎類型的默認值設置都是在init方法裏,就像這樣:
- (instancetype)init
{
self = [super init];
if (self) {
_height = 10;
_object = [[NSObject alloc] init];
_arrayM = [[NSMutableArray alloc] init];
}
return self;
}
複製代碼
我重寫了HLTestObject類的description
方法:
- (NSString *)description
{
NSString *result = @"";
result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
result = [result stringByAppendingFormat:@" height = %d,",self.height];
result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
result = [result stringByAppendingFormat:@" object = %p,",self.object];
return result;
}
複製代碼
仍是用上面的測試代碼,測試結果是這樣的:
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <HLTestObject: 0x7f8a5b458450> height = 20, arrayM = 0x7f8a5b422940, object = 0x7f8a5b4544e0,
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <HLTestObject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b4552e0, object = 0x7f8a5b45a710,
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <HLTestObject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b459770, object = 0x7f8a5b4544e0,
複製代碼
能夠看到,儘管使用的是同一個示例,但是他們的property值卻不同。 由於儘管沒有爲示例從新分配內存空間,可是由於又執行了init方法,會致使property被從新初始化。
因此咱們須要修改單例的實現。
第一種:
能夠將property的初始化或者默認值設置放到dispatch_once 的block內部:
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
instance.height = 10;
instance.object = [[NSObject alloc] init];
instance.arrayM = [[NSMutableArray alloc] init];
});
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
- (NSString *)description
{
NSString *result = @"";
result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
result = [result stringByAppendingFormat:@" height = %d,",self.height];
result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
result = [result stringByAppendingFormat:@" object = %p,",self.object];
return result;
}
複製代碼
來看看測試結果:
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <HLTestObject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <HLTestObject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <HLTestObject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,
複製代碼
第二種:
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
return [[self alloc] init];
}
- (instancetype)init
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super init];
instance.height = 10;
instance.object = [[NSObject alloc] init];
instance.arrayM = [[NSMutableArray alloc] init];
});
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
- (NSString *)description
{
NSString *result = @"";
result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
result = [result stringByAppendingFormat:@" height = %d,",self.height];
result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
result = [result stringByAppendingFormat:@" object = %p,",self.object];
return result;
}
複製代碼
測試結果:
2016-05-23 13:31:44.824 PractiseProject[3939:100662] <HLTestObject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,
2016-05-23 13:31:44.825 PractiseProject[3939:100662] <HLTestObject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,
2016-05-23 13:31:44.825 PractiseProject[3939:100662] <HLTestObject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,
複製代碼
注意: 以上代碼均是使用ARC的方式管理內存,若是你還在使用MRC(這也太不與時俱進了)。那你還須要重寫 retain 和release方法,防止示例引用計數的改變。
利用Swift中的一些特性,Swift中的單例能夠超級簡單,like this:
class HLTestObject: NSObject {
static let sharedInstance = HLTestObject();
}
複製代碼
但是這樣就完了麼?一樣寫一段測試代碼:
let object1 = HLTestObject.sharedInstance;
print(object1);
let object2 = HLTestObject();
print(object2);
複製代碼
打印結果倒是這樣的:
<SwiftProject.HLTestObject: 0x7f90ebc74e50>
<SwiftProject.HLTestObject: 0x7f90ebe5cf40>
複製代碼
因此,咱們必須禁用到構造方法:
class HLTestObject: NSObject {
static let sharedInstance = HLTestObject();
private override init() {
}
}
複製代碼
若是有實例屬性須要初始化,就能夠這樣:
class HLTestObject: NSObject {
var height = 10;
var arrayM: NSMutableArray
var object: NSObject
static let sharedInstance = HLTestObject();
private override init() {
object = NSObject()
arrayM = NSMutableArray()
super.init()
}
}
複製代碼
固然,因爲Swift的特性,在Swift中建立單例的方式也不止一種,須要注意的是要確保該類有且僅有一個實例就OK了。
Have Fun!