单例模式-如何保证单例对象全局唯一性?
程序员文章站
2022-05-18 14:16:34
...
单例概念:整个应用或系统只能有该类的一个实例,即是在整个项目中,这个类的对象只能被初始化一次。单例类保证了应用程序的生命周期中有且仅有一个该类的实例对象,而且易于外界访问。
“单例模式中,怎么保证这个单例对象是唯一的。或者说如果在一个对外开放的SDK中,怎么才能保证用户获得的对象是唯一的单例?”
当被问到这个问题的时候,其实是有点懵的。单例模式的单例对象不就是唯一的吗?否则怎么称之为单例模式?带着疑问,写出了常用的单例模式。
static UserDataManager *_shareManager;
+ (instancetype)shareManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shareManager = [[UserDataManager alloc] init];
});
return _shareManager;
}
心想,都已经这个样子了,怎么会不唯一呢?那么问题出现在哪里?很容易发现通过类方法shareManager
获取的单例对象是唯一的。但是只有这一种方法才能获取单例类的实例化对象吗?
并不是!
比如说,通过alloc
初始化的对象。它是单例类的对象,但它并不是单例对象。所以就出现了不唯一的状况,尤其是在使用者不知情的情况下,难免会出现问题。
再比如,如果实现了NSCopying
协议,通过copy方法得到单例对象的复制体,也会产生不唯一的单例类的对象。
所以,上面那种写法并没有错,只是忽视了一个很重要的问题:保证单例对象的唯一性,限制其它状况的产生。
解决方法一:默默付出,将初始化与拷贝的对象指向单例对象
static UserDataManager *_shareManager;
+ (instancetype)shareManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shareManager = [[super allocWithZone:NULL] init];
});
return _shareManager;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [UserDataManager shareManager];
}
- (id)copyWithZone:(NSZone *)zone {
return [UserDataManager shareManager];
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [UserDataManager shareManager];
}
测试结果:
UserDataManager *manager = [[UserDataManager alloc] init];
manager.userName = @"Json";
[UserDataManager shareManager].userName = @"李四";
NSLog(@"%@",manager);
NSLog(@"%@",[UserDataManager shareManager]);
UserDataManager *managerCopy = [[UserDataManager shareManager] copy];
NSLog(@"%@",managerCopy);
// <UserDataManager: 0x60000374c5f0>
// <UserDataManager: 0x60000374c5f0>
// <UserDataManager: 0x60000374c5f0>
这种情况下通过alloc
、copy
的方式获取的依旧是唯一的单例对象。
解决方法二:强势限制,alloc
、copy
方法中抛异常
如果没有实现NSCopying
协议与copyWithZone:
方法,调用copy
会报错。所以这里可以只对alloc
方法中设置异常提示。
+ (instancetype)alloc {
if (_shareManager) {
NSException *exception = [NSException exceptionWithName:@"重复创建单例对象" reason:@"未使用单例方法" userInfo:nil];
[exception raise];
}
return [super alloc];
}
当程序*中止,就知道有些对象是单例对象,并不是随随便就能用的。
单例对象从初始化开始,在程序的整个证明周期内存在。
上一篇: 【Qt每天一例】5.打印Qt版本