说道设计模式我想只要涉及编程的人都不会陌生,他有20多种之多,今天我主要说明一下iOS下的一种应用非常普遍的设计模式--单例模式。首先看下图,我想会给你一些启发的。
图1 单例和普通实例的比较
没错,单例和普通实例最大的区别就是,单例模式的全局只有一个实例,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,而这样的类我们称之为单例类。
Singleton(单例模式),也叫单子模式,是一种常见的软件设计模式。在应用这个单例时,单例对象的类必须保证只有一个实例的存在。许多时候整个系统只需要拥有一个全局对象,这样有利于协调系统整体的行为。例如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些数据有一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
实现单例模式的的思路是:一个类能返回对象一个实例(永远是同一个)和一个获得该实例的方法(必须是静态方法(也叫类方法));当调用这个方法时,如果类持有的实例不为空,就返回这个实例;如果类保持的实例为空,就创建该类的实例并将实例赋予该类保持的实例,从而限制用户只有通过该类提供的静态方法来得到该类的唯一实例。如图2
图2 单例的实现
单例模式的优点:
- 单例模式在内存中只有一个实例,减少了内存开支。特别是一个对象需要频繁的创建、销毁时,而创建与销毁的性能又无法优化,单例模式的优势就非常明显。
- 单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 单例模式可以避免对资源的多重占用。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问。
单例模式的缺点:
- 单例模式一般没有接口,扩展很困难,除了修改代码基本上没有第二种途径实现。
- 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的。
- 单例模式与单一职责原则有冲突。
单例模式在IOS中的应用也是非常广泛的,例如[NSNotificationCenterdefaultCenter]、[UIApplicationsharedApplication]、[NSFileManagerdefaultManager]等等,它在iOS开发中有着举足轻重的作用,使用单例能很轻松的解决很多问题。然而,在iOS开发中,单例要注意分情况使用:
1. 在非ARC(Automatic ReferenceCounting)情况下:
1)、实现的方法是:
01. 为单例类声明一个静态的实例变量,并且初始化它的值为nil。
02. 在获取实例的方法中,只有在静态实例为nil的时候,产生一个类的实例,这个实例通常称为共享的实例。
03. 重写allocWithZone:方法,用于确定不能够使用其他方法创建类的实例,限制用户只能通过获取实例的方法得到这个类的实例。所以,在allocWithZone:方法中直接返回共享的类实例。
04. 实现基本的协议方法copyWithZone:、release、ratain、retainCount和autorelease,用于保证单例具有一个正确的状态。
具体实现代码如下:
@implementation NYHSingleton
static NYHSingleton *instance = nil;
+ (NYHSingleton *)sharedInstance
{
if (instance == nil) {
instance = [[super allocWithZone:NULL] init];
}
return instance;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone*)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
returnNSUIntegerMax;
}
- (oneway void)release
{
// 不做处理
}
- (id)autorelease
{
return self;
}
从代码可以看到这种方式,使用静态成员维持了一个永久存在的对象,重载了allocWithZone:方法,并且覆盖了所有与引用技术有关的方法,这都使得这个对象不会被销毁,使用者哪怕使用alloc时也会返回唯一的实例(alloc方法中会先调用allocWithZone:方法创建对象)。但是有很大的一个问题,那就是多线程的问题,如果是在多线程中那么该种方法就不能保证只产生一个对象了。所以涉及到多线程时,必须小心使用。例如,当唯一实例尚未创建时,如果有两个线程同时调用创建方法,那么他们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中的实例唯一的原则。解决这个问题的办法是为标记类是否已经实例化的变量提供一个互斥锁(但是这样会降低效率)。
具体实现代码如下:
static NYHSingleton *instance = nil;
+ (NYHSingleton *)sharedInstance
{
// 为了确保多线程情况下,仍然确保实体的唯一性
@synchronized (self){
if (instance == nil) {
instance = [[super allocWithZone:NULL] init];
}
}
return instance;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone*)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
returnNSUIntegerMax;
}
- (oneway void)release
{
// 不做处理
}
- (id)autorelease
{
return self;
}
2)、上面也说到了,在多线成环境下,加上互斥锁(@synchronized),虽然可以解决问题,但是耗费性能和内存,尤其是在手机软件的开发中(手机内存实在是少的可怜),所以就出现了下面的这种方法:GCD+Block
具体请看代码:
static NYHSingleton *instance = nil;
+ (NYHSingleton *)sharedInstance {
// 保证只执行一次
static dispatch_once_tonceToken;
dispatch_once (&onceToken, ^{
instance = [[selfalloc] init];
});
return instance;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone*)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
- (oneway void)release
{
// 不做处理
}
- (id)autorelease
{
return self;
}
这种是用GCD(Grand Central Dispatch)的方式实现的,方便快捷,也不用担心多线程的问题,但是它也有个小缺点,就是它并不能阻止人们通过alloc
方法来实例化一个对象,所以这并不是严格意义上的单例模式,但是一般程序都是我们自己写的,我们自己记得就好了,这也没什么可担心的。
2. 在ARC(Automatic ReferenceCounting)情况下
通过ARC+GCD+Block的方法来实现单例模式是非常简单而愉悦的
代码如下:
+ (NYHSingleton *)sharedInstance
{
static NYHSingleton *instance = nil ;
static dispatch_once_tonceToken;
dispatch_once (&onceToken, ^ {
instance = [[self alloc] init];
});
return instance;
}
为了简化使用ARC+GCD+Block来创建单例,可以定义下面这样的一个宏:
#define DEFINE_SHARE_INSTANCE_USING_BLOCK(block) \ staticdispatch_once_tonceToken; \ __strong static id sharedInstance = nil; \ dispatch_once(&onceToken,^{ \ sharedInstance = block(); \ }); \ return sharedInstance; \
实例化的实现方法如下所示:
+ (NYHSingleton*) sharedInstance{ DEFINE_SHARE_INSTANCE_USING_BLOCK(^{
return [[selfalloc] init];
}); }
我认为以后用ARC+GCD+Block来创建单例模式会越来越受欢迎,在iOS7发布后,APPLE的ARC机制是越来越完善了,而且自Xcode5.0以后,iOS项目默认就是ARC机制,这省去了我们很多的时间去手动管理内存。
仅供参考:
object-c 单例模式(包括ARC)
http://www.apkbus.com/android-128097-1-1.html
IOS设计模式之一:单例模式
http://www.cnblogs.com/limlee/archive/2012/06/13/2547310.html
iOS设计模式——单例模式
http://blog.csdn.net/lovefqing/article/details/8516536
objective-c 单例模式详解
http://my.oschina.net/superchen/blog/92034
苹果官方的单例写法
iOS设计模式之 Singleton(单例模式)
http://jobleeimay.blog.163.com/blog/static/212532289201302473652537/
iOS设计模式(02):单例模式
http://beyondvincent.com/blog/2013/05/09/20/
IOS设计模式浅析之单例模式(Singleton)
http://www.cnblogs.com/eagle927183/p/3457538.html
iOS单例的两种实现
http://webfrogs.me/2013/04/01/ios-singleton/
【iOS】Objective-C简约而不简单的单例模式
http://leyteris.iteye.com/blog/1669198
Objective-C单例模式写法以及单例模式模板宏
http://blog.csdn.net/kindazrael/article/details/7917863
23种设计模式——单例模式
http://wtlucky.github.io/geekerprobe/blog/2013/03/19/singleton/
利用dispatch_once创建单例
http://bj007.blog.51cto.com/1701577/649413