概念
在程序运行过程,一个类只有一个实例
使用场合
在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
ARC实现单例
- 在类的内部提供一个static修饰的全局变量
- 提供一个类方法,方便外界访问
- 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间
- 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法
源码
<NSCopying>的协议,并实现相应的方法,防止别人误调copy方法而崩溃
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CustomManager : NSObject
+ (instancetype)new NS_UNAVAILABLE;
//命名规范 share+类名 | default+类名 | 类名
+ (instancetype)shareCustomManager;
@end
NS_ASSUME_NONNULL_END
@interface CustomManager ()<NSCopying>
@end
@implementation CustomManager
// 提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static CustomManager *_manager;
// 类方法,返回一个单例对象
+(instancetype)shareCustomManager
{
// 注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
return [[self alloc]init];
}
// new -> alloc -> allocWithZone 最终必须调用的方法
// 保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
// 使用GCD中的一次性代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_manager = [super allocWithZone:zone];
});
// 使用加锁的方式,保证只分配一次存储空间
@synchronized(self) {
if (_manager == nil) {
_manager = [super allocWithZone:zone];
}
}
return _manager;
}
// 1. mutableCopy 创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
// 2. copy 返回一个不可变对象。分两种情况:
//(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加1;
//(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
return [[self class] allocWithZone:zone];
return _manager;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _manager;
}
@end
单利创建的几种方式
1、传统方法
//传统方式
+(instancetype)traditionSingleton{
static CustomManager *singleton = nil;
if (singleton == nil){
singleton = [[CustomManager alloc] init];
}
return singleton;
}
2、GCD方式
//GCD方式
+(instancetype)gcdSingleton{
static CustomManager *singleton = nil;
//给单例加了一个线程锁
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[CustomManager alloc] init];
});
return singleton;
}
3、加锁方式
//加锁方式
+(CustomManager*)singleton {
static CustomManager* singleton;
@synchronized(self) {
if (!singleton) {
singleton = [[CustomManager alloc] init];
}
return singleton;
}
return nil;
}
扩展
Q:new -> alloc -> allocWithZone 最终必须调用的方法
A: 通过打印方式查看调用方法;
+[CustomManager new]
+[CustomManager alloc]
+[CustomManager allocWithZone:]
说明
避免使用new创建对象,从安全和设计角度来说我们应该对初始化所有属性,提高程序的健壮性和复用性。
不论是何种情况,在类中至少包含一个构造函数是一种很好的编程实践,如果类中有属性,好的实践往往是初始化这些属性。
——以上摘自《The Object-Oriented Thought Process》 by Matt Weisfeld
更新记录
版本 | 时间 |
---|---|
2.1 | 2019年01月30日14:29:26 |
2.0 | 2018年12月25日11:18:02 |
网友评论