单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
在Java中我们定义一个单例类:
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {
// Exists only to defeat instantiation.
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// Other methods...
}
在ios中我们按照java那样定义一个单例类,
static DataModel *_sharedDataModel=nil;
@implementation DataModel
+(DataModel *)sharedDataModel
{
if (!_sharedDataModel)
{
_sharedDataModel=[[self alloc] init];
}
return _sharedDataModel;
}
@end
DataModel *model1=[[DataModel alloc] init];
DataModel *model2=[DataModel sharedDataModel];
NSLog(@"model1:%@",model1);
NSLog(@"model2:%@",model2);
打印内存地址,我们发现内存地址不一样,也就是说它们并没有共用一个内存,
2014-02-24 14:31:57.734 IOSStudy[833:a0b] model1:<DataModel: 0x8e24600>
2014-02-24 14:31:57.735 IOSStudy[833:a0b] model2:<DataModel: 0x8e24610>
显然在ios中这样定义单例类是不对的,查相关资料,发现在objective-c中要实现一个单例类,至少需要做以下四个步骤:
1、为单例对象实现一个静态实例,并初始化,然后设置成nil,
2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例,
3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例,
4、适当实现allocWitheZone,copyWithZone,release和autorelease。
疑问来了,allocWithZone是什么,和alloc有什么区别,为什么要重写这个方法,查相关资料得知
在NSObject 这个类的官方文档里面,allocWithZone方法介绍说,该方法的参数是被忽略的,正确的做法是传nil或者NULL参数给它。而这个方法之所以存在,是历史遗留原因。
Do not override allocWithZone: to include any initialization code. Instead, class-specific versions of init… methods.
This method exists for historical reasons; memory zones are no longer used by Objective-C.
文档里面提到,memory zone已经被弃用了,只是历史原因才保留这个接口。详细是什么历史原因我没找到,不过后面介绍的内容会稍微涉及到。
而实践证明,使用alloc方法初始化一个类的实例的时候,默认是调用了 allocWithZone 的方法。于是覆盖allocWithZone方法的原因已经很明显了:为了保持单例类实例的唯一性,需要覆盖所有会生成新的实例的方法,如果有人初始化这个单例类的时候不走[[Class alloc] init] ,而是直接 allocWithZone, 那么这个单例就不再是单例了,所以必须把这个方法也堵上。
那么下面我们看看如何正确定义一个单例类:
static DataModel *_sharedDataModel=nil;
@implementation DataModel
+(DataModel *)sharedDataModel
{
//确保线程安全
@synchronized(self)
{
if (_sharedDataModel==nil)
{
[[self alloc] init];
}
return _sharedDataModel;
}
return _sharedDataModel;
}
//重新allocWithZone方法
+(id) allocWithZone:(struct _NSZone *)zone
{
@synchronized(self)
{
if (_sharedDataModel==nil)
{
_sharedDataModel=[super allocWithZone:zone];
return _sharedDataModel;
}
}
return nil;
}
//确保copy对象也是唯一的
-(id) copy
{
return self;
}
//确保计数唯一
-(id) retain
{
return self;
}
-(unsigned) retainCount
{
return UINT_MAX;
}
//oneway以表示该方法是用于异步消息的
-(oneway void) release
{
}
-(id) autorelease
{
return self;
}
DataModel *model1=[[DataModel alloc] init];
DataModel *model2=[DataModel sharedDataModel];
DataModel *model3=[model1 copy];
NSLog(@"model1:%@",model1);
NSLog(@"model2:%@",model2);
NSLog(@"model3:%@",model3);
2014-02-24 15:31:53.017 IOSStudy[1114:a0b] model1:<DataModel: 0xa063c30>
2014-02-24 15:31:53.018 IOSStudy[1114:a0b] model2:<DataModel: 0xa063c30>
2014-02-24 15:31:53.019 IOSStudy[1114:a0b] model3:<DataModel: 0xa063c30>
我们看打印结果,内存地址一样,说明DataModel类只初始化一次
貌似在ARC模式下,还有这种方式
@implementation DZSinglonARC
+ (DZSinglonARC*) shareInstance
{
static DZSinglonARC* share = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
share = [[super allocWithZone:NULL] init];
});
return share;
}
+ (instancetype) allocWithZone:(struct _NSZone *)zone
{
return [self shareInstance];
}
@end
如果还有其他的一些定义单例类的方式,大家可以说出来,相互学习~~~~