要解决的问题
要确保应用中的一个特定类仅有一个实例,并提供好一个全局的访问点。一般是设计约束或者是为了控制对有限资源的访问,
实现方案
在第一次调用类的构造函数时就会创建单一的全局实例,接下来调用构造函数时会检查该全局实例是否存在,如果存在就返回该实例的引用而不是创建一个新的对象。
单例的一般写法:
首先定义一个全局的实例;
static MyClass *instance = nil;
然后提供一个类方法实现对实例的访问;
+ (MyClass *)sharedInstance
{
@synchronized(self) {
if (!instance) {
instance = [[MyClass alloc] init];
}
}
return instance;
}
这样就可以实现对全局变量的唯一访问了。
但是,仅限于调用这个方法访问的时候是唯一的,如果通过alloc ,init方法来生成实例的时候还是会创建一个新的实例,所以为了保证全局变量访问的唯一性,应该再实现以下两个方法。
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
如果项目用的是非ARC的话,还需要重写合适的方法来避免释放全局实例,总的来说,目的就是保证只有一个实例而且引用计数只能是1。
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
- (oneway void)release
{
// Do nothing
}
- (id)autorelease
{
return self;
}
以上就是一个完整的单例模式实现了。
下面提供一个简化版本,
static MyClass *instance = nil;
+ (MyClass *)sharedInstance
{
@synchronized(self) {
if (!instance) {
instance =[MyClass new];
}
}
return instance;
}
这样实现的单例是不安全的。但是,如果是你在使用这个单例的时候,知道单例不能被释放或者保留。此外,需要确保在启动其他外部线程之前完成全局实例的初始化。尽管立刻从多个线程读取实例变量的内容是安全的,但是写入是不安全的。因此,必须确保对共享实例方法的第一次访问必须在创建任何可能需要访问该全局实例的线程之前完成。这个版本就足够了。
自从苹果引入了GCD(Mac OS 10.6和iOS4.0)之后,单例又有一种实现方式。
+(MyClass *)sharedInstance
{
static MyClass *intance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
intance = [[MyClassalloc] init];
});
return intance;
}
函数voiddispatch_once( dispatch_once_t *predicate, dispatch_block_t block);其中第一个参数predicate,该参数是检查后面第二个参数所代表的代码块是否被调用的谓词,第二个参数则是在整个应用程序中只会被调用一次的代码块。dispach_once函数中的代码块只会被执行一次,而且还是线程安全的。
OK,Done。