在iOS OC中,一般我们都是用官方推荐的写法来写单例:GCD方式单例
单例:整个程序只创建一次,全局共用。
1. ****单例的创建****
// SharedPerson.h 文件中
+ (instancetype)share;
// SharedPerson.m 文件中
static SharedPerson *_person;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
_person = [super allocWithZone:zone];
});
return _person;
}
+ (instancetype)share {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [[self alloc]init];
});
return _person;
}
- (id)copyWithZone:(NSZone *)zone {
return _person;
}
- 分析单例
-
static SharedPerson *_person
: 利用static关键字来确保创建的单例对象_person只能在此类中可以访问到;将_person定义成全局变量为了确保其生命周期存在于整个程序中,期间不被销毁.
-
-
dispatch_once
: 此函数确保_person对象在程序中只会创建一次,并且也保证了创建时的线程安全。
-
-
+ (instancetype)share
: 提供便捷的类方法来创建单例对象,强烈建议用此法创建对象。
-
-
+ (instancetype)allocWithZone:(struct _NSZone *)zone : [SharedPerson alloc]
分配对象内存时,实际会调此函数allocWithZone:(struct _NSZone *)zone
,所以需要重写此方法来保证单例对象只会创建一次,而且是必须重写此方法,防止其他开发者直接用初始化方法来创建单例对象。
-
-
- (id)copyWithZone:(NSZone *)zone
: 此函数来保证单例对象可以copy再得到一个一模一样的对象。
-
3、一般使用:
+ (instancetype)sharedInstance;
+ (instancetype)sharedInstance {
static URLManager * ins = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
ins = [[URLManager alloc] init];
});
return ins;
}
URLManager* a = [URLManager sharedInstance];
接下来,可以用 a 这个单例来干很多事情了,看起来没有问题。在很多实际的项目中,很多人也的确是这么做的。
可是,谁能保证所有人都会使用 sharedInstance 方法来创建对象?而一旦有人用 alloc,new 等来创建对象,这就不是单例了。
例如:
URLManager* a = [URLManager sharedInstance];
URLManager* b = [[URLManager alloc] init];
URLManager* c = [URLManager new];
查看 a、b、c :
截屏2022-03-30 14.59.30.png
可以看到,a、b、c 不是同一个对象,而所谓单例,就是不管我用何种方法创建对象,都必须是同一个。
所以,单例模式,绝不是一个 sharedInstance 就够了。
解决方案 一、
那么如何避免这种问题呢?我们知道:
在对象创建的时候,无论是 alloc 还是 new,都会调用到 allocWithZone: 方法;
使用拷贝创建对象时,会调用 copyWithZone:,mutableCopyWithZone:方法;
那么,重写这些方法,就可以让创建的对象唯一。
+ (instancetype)sharedInstance {
static URLManager * ins = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
ins = [[super allocWithZone:nil] init];
});
return ins;
}
+(id)allocWithZone:(NSZone *)zone{
return [self sharedInstance];
}
-(id)copyWithZone:(NSZone *)zone{
return [[self class] sharedInstance];
}
-(id)mutableCopyWithZone:(NSZone *)zone{
return [[self class] sharedInstance];
}
再运行,看一看a、b、c:
截屏2022-03-30 15.03.01.png
都是同一个对象!大功告成!
解决方案 二、
此外,还有一种方法,就是直接禁用掉 alloc、new 、copy等方法:
+(instancetype) alloc __attribute__((unavailable("replace with 'sharedInstance'")));
+(instancetype) new __attribute__((unavailable("replace with 'sharedInstance'")));
-(instancetype) copy __attribute__((unavailable("replace with 'sharedInstance'")));
-(instancetype) mutableCopy __attribute__((unavailable("replace with 'sharedInstance'")));
如此,在调用这些方法时就会报错,提示使用 sharedInstance 方法:
截屏2022-03-30 15.04.45.png
以此,也可以达到单例模式的要求,始终只有一个对象。
网友评论