单例模式分为两种,一种是只允许存在一个实例,无论通过什么方法创建的时候都只返回同一个实例。另一种就是除了使用单例之外,也允许用户创建其他的实例。第一种如[UIApplication sharedApplication],第二种如[NSFileManager defaultManager]。
一般的单例实现方法
static MySingletonClass *sharedInstance;
+(MySingletonClass *)sharedSingletonClass
{
if (sharedInstance == nil)
{
sharedInstance = [[super allocWithZone:nil] init];
}
return sharedInstance;
}
这是一个简单又快捷的方式,但它不是线程安全的。因为当多个线程同时访问它的时候,它可能会被调用多次。那么我们就需要一个方法来保证这个构造方法最多只被调用一次。
线程安全的方式
+(MySingletonClass *)sharedSingletonClass
{
@synchronized(self) {
if (sharedInstance == nil)
{
sharedInstance = [[super allocWithZone:nil] init];
}
}
return sharedInstance;
}
通过使用synchronized关键字来达到线程互斥的目的,这样sharedInstance = [[super allocWithZone:nil]init]就会只调用一次了。
我们保证了线程的同步问题,但是如果我们只想拥有一个实例怎么办呢?因为目前情况下,用户还可以通过其他方式来创建另一个实例。
只有一个实例
为了不让用户自己创建其他的实例,我们需要将其他创建实例的方法都重写一下。
+ (id)allocWithZone:(NSZone *)zone
{
return [[<span style="font-family: Arial, Helvetica, sans-serif;">MySingletonClass<span style="white-space:pre"> </span> </span>sharedSingletonClass] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;[NSFileManager defaultManager]
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
看到这里大家有没有发现我们只重写了allocWithZone而没有重写alloc方法呢,会不会觉得通过alloc还可以创建一个新的实例呢?哈哈,这样想你就错了,因为alloc最终是调用allocwithZone来创建实例的,so,不用担心这个问题了。
另外,不知道大家注意到没有,在sharedSingletonClass里面用的时候[super allockWithZone]而没有用[self alloc],如果使用后者的话会形成死循环哦,因为[self alloc]会调用allockWithZone,但我们重写了这个函数,在这个函数里又调用了sharedSingletonClass。
另外,大家请仔细想想为什么我们需要重写retain、retainCount、release和autoRelease方法呢?
到目前为止,我们基本上可以创建我们想要的单例了。但是在iOS中,引入了ARC,retain和release都不允许在代码中出现,这个时候该怎么办呢,是不是不用重写跟内存相关的代码就可以了呢?
另一种线程同步机制
+(MySingletonClass *)sharedSingletonClass
{
static dispatch_once_t pred = 0;
__strong static MySingletonClass *sharedInstance;
dispatch_once(&pred, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
dispatch_once是指在应用程序的生命周期内,这个方法只被执行一次。
在ARC模式下就不能再重写retain和release相关的函数了,只需要实现面下的即可。需要将retain和release相关的代码去掉,其他的与普通的一样实现即可。因为在ARC环境下不会有显式的retain和release的调用,内存完全由程序来管理,所以不会造成对象被销毁的问题。