以我自己做过的项目来看,单例模式(singleton)在所有设计模式中应该是用得频繁的。其中心思想和作用也是在设计模式中最简单的,在接触这这个模式后没有程序员不会喜欢它。好了,废话不多说,立即看一下在Objective-C语言里是怎么实现单例模式。在Objective-C中实现单例和其它语言实现的思想基本一致。
苹果官方实现方法
在苹果的官方文档库里可以找到一篇关于如何创建单例的一些建议(Creating a Singleton Instance)(但时代有点久远了,里面的内容也有点过时,这篇文章也就被苹果遗弃了)
先给里面给的建议写出来:
在Objective-C 中要实现一个单例类,至少需要做以下四个步骤:
1、为单例对象实现一个静态实例,并初始化,然后设置成nil;
2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例;
3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例;
4、适当实现allocWithZone,copyWithZone,release和autorelease。
注: 单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。
先看看与官方例子差不多的代码:
/* Singleton.h */
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
+ (Singleton *)sharedInstance;
@end
/* Singleton.m */
#import "Singleton.h"
static Singleton *instance = nil;
@implementation Singleton
+ (Singleton *)sharedInstance {
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
return instance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init {
if (instance) {
return instance;
}
self = [super init];
return self;
}
- (id)retain {
return self;
}
- (oneway void)release {
// Do nothing
}
- (id)autorelease {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax;
}
@end
这段代码看起来很多,它是建立在非ARC(Automatic Reference Counting)环境下,而且并不是线程安全的。
我们先实现一下ARC环境下的单例模式
1.首先来个简单的,看如下代码:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static Singleton * sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[Singleton alloc] init];
});
return sharedInstance;
}
说明:
使用了Objective-C中的GCD(Grand Central Dispatch)解决方案,其中利用了dispatch_once的特性。
但是,这种方法还有个缺陷,因为在其它地方可通过alloc和copy任意创建对象,所以把这两个方法也重写一下,而且原来实现的方式也要改一下,这样就有了第二个版本。
2.改良后的版本
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static Singleton * sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
说明:
重写了allocWithZone和copyWithZone后,调用alloc和copy消息后也不会重复创建新的对象了。如果还要防止调用mutableCopy的话,还要重写mutableCopyWithZone函数,不过如果默认不实现的话,调用mutableCopy是会有异常的,程序会立即崩溃掉。
但是这个实现也有问题,就是init函数也会有可能被调用多次。所以有了以下版本。
3.再改良后的版本
@implementation Singleton
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static Singleton * sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] initPrivate];
});
return sharedInstance;
}
+ (instancetype)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (instancetype)copyWithZone:(NSZone *)zone {
return self;
}
- (instancetype)init {
return self;
}
- (instancetype)initPrivate {
if (self = [super init]) {
// do the stuff
}
return self;
}
@end
根据非ARC版我们可以很容易实现一个ARC版
@implementation Singleton
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static Singleton * sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] initPrivate];
});
return sharedInstance;
}
+ (instancetype)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (instancetype)copyWithZone:(NSZone *)zone {
return self;
}
- (instancetype)init {
return self;
}
- (instancetype)retain {
return self;
}
- (oneway void)release {
// Do nothing
}
- (instancetype)autorelease {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax;
}
- (instancetype)initPrivate {
if (self = [super init]) {
// do the stuff
}
return self;
}
@end
然后我们可以用宏封装一下
用ARC实现的单例和没用ARC的单例有一些不一样的地方,要注意。所以如果用到宏的地方可以用以下宏来区别ARC环境下和非ARC环境下的宏:
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif
用宏实现:
#define SINGLETON_IN_HEADER(className) +(instancetype)sharedInstance;
// Begin define SINGLETON_IN_IMPLEMENTATION
#if __has_feature(objc_arc)
// ARC version
#define SINGLETON_IN_IMPLEMENTATION(className) \
+ (instancetype)sharedInstance {\
static dispatch_once_t onceToken;\
static className * sharedInstance = nil;\
\
dispatch_once(&onceToken, ^{\
sharedInstance = [[super allocWithZone:NULL] initPrivate];\
});\
\
return sharedInstance;\
}\
\
+ (instancetype)allocWithZone:(NSZone *)zone {\
return [self sharedInstance];\
}\
\
- (instancetype)copyWithZone:(NSZone *)zone {\
return self;\
}\
\
- (instancetype)init {\
return self;\
}
#else
// MRC version
#define SINGLETON_IN_IMPLEMENTATION(className) \
+ (instancetype)sharedInstance {\
static dispatch_once_t onceToken;\
static className * sharedInstance = nil;\
dispatch_once(&onceToken, ^{\
sharedInstance = [[super allocWithZone:NULL] initPrivate];\
});\
return sharedInstance;\
}\
\
+ (instancetype)allocWithZone:(NSZone *)zone {\
return [self sharedInstance];\
}\
\
- (instancetype)copyWithZone:(NSZone *)zone {\
return self;\
}\
\
- (instancetype)init {\
return self;\
}\
\
- (instancetype)retain {\
return self;\
}\
\
- (oneway void)release {\
}\
\
- (instancetype)autorelease {\
return self;\
}\
\
- (NSUInteger)retainCount {\
return NSUIntegerMax;\
}
#endif // End define SINGLETON_IN_IMPLEMENTATION