什么是单例模式:
单例模式是一个类在程序中只有一个实例对象。我们可以从全局的一个入口点对这个实例对象进行访问,通常,单例模式的实例对象是类对象。在iOS中,单例模式是一种非常有用的设计模式。
Cocoa 和 Foundation框架中也常见单例模式的使用,我们最熟悉的UIApplication:当程序启动的时候,会调用main方法,在该方法中,会实例化一个UIApplication对象,之后我们在程序中任意地方调用sharedApplication方法都将返回一个与当前应用程序相关的UIApplication实例。
当然,在iOS SDK中,根据特定的需求,有些类不仅提供了单例的访问接口,还提供了实例化一个新对象的接口。例如,NSFileManager可以通过defaultManager方法返回相同的一个单例对象。同时,如果我们需要一个新的实例对象,我们还可以通过init方法来创建。
单例模式的使用场景:
在程序中,单例模式经常希望一个类只有一个实例,并且提供类方法来创建这个实例,我们通过这个类方法访问的实例有且只有一个。
首先我们看看单例模式的实现:
分为ARC和非ARC
这里,我们只讨论ARC+GDC的方法来实现,先看看代码
+ (id)sharedManager {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
在上面的代码中,我们调用Grand Central Dispatch(GCD)中的dispatch_once方法就可以保证我们的类只被实例化一次。既然有且只有一个,我们就会面临着资源竞争,这里就需要考虑线程安全。由于dipatch_once该方法是线程安全的,所以我们就不用担心在不同的线程中,会获得不同的实例。
说明:这里我们只能保证该实例的创建方法,sharedManager是线程安全的,并不能保证该单例中所有的方法都是线程安全的。
这里涉及到一个线程安全相关的概念:(摘自百度百科)
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态变量引起的。*
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
类要成为线程安全的,首先必须在单线程环境中有正确的行为。*
此外,一个类要成为线程安全,在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排或交错,它必须扔有上述正确的行为,并且在调用的代码中没有任何额外的同步。起效果就是,在所有线程看来,对于线程安全对象的操作都是固定的、全局一致的顺序发生的。
从特定的线程角度来看,由不同线程所执行的对象操作是先后而不是并行执行的。
当然,在ARC中,不用GCD也是可以做到线程安全的,代码如下:
@synchronized(self) {
if(sharedInstance==nil) {
sharedInstance= [[self alloc] init];
}
}
returnself;
}
使用ARC+GCD来创建单例,我们可以定义一个下面这样的一个宏
#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \
static dispatch_once_t onceToken =0;\
__strong static id sharedInstance = nil; \
dispatch_once(&onceToken, ^{ \
sharedInstance = block();\
});\
使用方法:
+(id)sharedManager {
DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
return [[self alloc] init];
});
}