昨天总结了swift的单例创建模式,今天总结一下OC单例的创建模式
单例用处:主要用在封装网络请求,播放器,存放常用数据。
单例特点:只初始化一次,生命和程序的生命周期相同,访问方便。
创建单例最初的学习到的写法是判断对象是否为空,为空才创建,但这仅仅是单线程安全,为了多线程安全,创建单例时会加锁,写法如下
+ (instancetype)shareManager{
@synchronized(self){
if (!manager) {
manager = [[self alloc]init];
}
return manager;
}
}
后来发现GCD创建单例更加简便
static DataManager *manager = nil;
@implementation DataManager
+ (instancetype)shareManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
});
return manager;
}
@end
此时单例就已经写好了,只要我们在外面调用shareManager这个方法,返回的对象始终是一个,因为dispatch_once只执行一次。
1、如果不小心调用了alloc、allocWithZone方法呢?此时的manager还是同一个对象吗?
答案:当然不是。
解决方法:参考从 Objective-C 里的 Alloc 和 AllocWithZone 谈起这篇文章加上事件证明,发现调用alloc
方法时,会自动调用allocWithZone
方法,此时只需要重写AllocWithZone
即可。
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
此时的DataManager.m
代码如下
#import "DataManager.h"
@interface DataManager()
@end
static DataManager *manager = nil;
@implementation DataManager
+ (instancetype)shareManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc]init];
});
return manager;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
@end
2、如果不小心调用了copy、mutableCopy方法呢?此时还是同一个对象吗?
答案:运行崩溃。
崩溃原因:提示[DataManager copyWithZone:]: unrecognized selector sent to instance 0x600003b71710'
,因为对应自己定义的类,要实现copy
就要默认实现NSCopying
协议,同理实现mutablecopy
就要实现NSMutableCopying
协议。
解决办法:那就实现NSCopying的方法- (id)copyWithZone:(nullable NSZone *)zone;
和NSMutableCopying的方法- (id)mutableCopyWithZone:(nullable NSZone *)zone;
因为要保持manager
始终是一个单例,此时直接返回已经初始化的对象。
此时的DataManager.m代码如下(方式1)
#import "DataManager.h"
@interface DataManager()<NSCopying,NSMutableCopying>
@end
static DataManager *manager = nil;
@implementation DataManager
+ (instancetype)shareManager{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc]init];
});
return manager;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return manager;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return manager;
}
@end
到此一个没有属性的完整的单例创建就完成啦~~~
当然也有别的方法实现,就是在shareManager
方法里面,不添加dispatch_once
,这样子每调用一次[DataManager shareManager]
就会执行一次init
,那此时init
方法需要使用dispatch_once
,避免多次进行初始化,代码如下:(方式2)
#import "DataManager.h"
@interface DataManager()<NSCopying,NSMutableCopying>
@end
static DataManager *manager = nil;
@implementation DataManager
+ (instancetype)shareManager{
return [[self alloc] init];
}
- (instancetype)init{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super init];
});
return manager;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return manager;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return manager;
}
但是如果需要初始化单例的时候,初始化一些属性,
此时如果使用方式1
,就需要在shareManager
还有allocWithZone
都写上属性的初始化,这个时候,单例虽然唯一,但是属性的地址却不唯一咯!!!
所以在需要初始化属性的单例里面,请使用方式2
,直接在init方式里面初始化属性,此时无论使用shareManager
还是alloc
初始化单例后,单例和单例属性都是唯一的!!!
2020.3.9号更新
参考地址:
iOS 单例-2020.3.9查看到的资料
iOS-单例模式写一次就够了
iOS开发多线程篇—单例模式
从 Objective-C 里的 Alloc 和 AllocWithZone 谈起
Objective-C copy,看我就够了
喜欢就点个赞吧✌️✌️
有错之处,还请指出,感谢🙏🙏