在设计模式中有一个“单例模式”,对应的我们常常会设计“单例类”(或称单件类)。但在实际应用中,我们常常也需要使用“半单例”。下面我们具体谈谈单例和半单例,以及他们的用法和区别。
单例模式
单例模式(singleton)顾名思义,就是只有一个实例。
作为对象的创建模式[GOF95], 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
也就是说,对于一个单例类,不论实例化对象多少次,都只有一个对象实例,而且这个实例一定是一个全局的能被整个系统访问到。
下面是Objective-C中完整的单例类的实现:
Singleton.h
#import<Foundation/Foundation.h>
@interface Singleton : NSObject
+(id)shareInstance;
@end
#import "Singleton.h"
static Singleton * instance = nil;
@implementation Singleton
+(id)shareInstance
{
if(instance == nil)
{
instance = [[super allocWithZone:nil]init]; //super 调用allocWithZone
}
return instance;
}
+(id)allocWithZone:(NSZone *)zone
{
return [Singleton shareInstance];
}
//可写可不写
- (id)init
{
if (instance)
{
return instance;
}
self = [super init];
return self;
}
-(id)copy
{
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
-(id)retain
{
return self;
}
- (oneway void)release
{
// Do nothing
}
- (id)autorelease
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
@end
解释说明:
1.static Singleton * instance = nil;
静态全局变量,始终指向实例化出的对象。
2.+(id)shareInstance;
外界初始化得到单例类对象的唯一借口,这个类方法返回的就是instance,即类的一个对象,
如果instance为空,则实例化一个对象,如果不为空,则直接返回。这样保证了实例的唯一。
3.-(id)copy;
- (id)copyWithZone:(NSZone *)zone;
这两个方法是为了防止外界拷贝造成多个实例,保证实例的唯一性。
4.-(id)retain;
因为只有一个实例对象,所以retain不能增加引用计数。
5.- (NSUInteger)retainCount;
因为只有一个实例对象,设置默认引用计数。这里是取的NSUinteger的最大值,当然也可以设置成1或其他值。6.- (onewayvoid)release;
oneway void是用于多线程编程中,表示单向执行,不能“回滚”,即原子操作。
半单例
1.半单例不同于单例,它可以实例化多个对象。
2.在程序中系统要求能访问到这个类的当前对象实例。
比如说:我们对于一个ViewController,我们可以实例化多个。在ViewController中,我们给他添加了很多视图View。
这些View中,当与用户发生某个交互时,我们由需要向Controller发送消息,实现响应操作。那么,View必须能找到当前的ViewController。这时,我们可以将ViewController设置成一个半单例类。
以“翻书”程序为例:这里涉及到两个类LeavesViewController 和LeavesView
显然,我们是在LeavesViewController中添加多个LeavesView实现多页效果,
当判断出LeavesView翻到最后一页时,我们需要让LeavesViewController响应,跳到下一个Controller,其他场景视图。
LeavesViewController类为“半单例”:
LeavesViewController.h
#import<UIKit/UIKit.h>
#import"LeavesView.h"
@interface LeavesViewController :UIViewController <LeavesViewDataSource,LeavesViewDelegate>
{
LeavesView *leavesView;
}
@property(nonatomic,retain)LeavesView *leavesView;
+ (id)shareInstance;
- (void)goToPlay;
@end
LeavesViewController.m
#import"LeavesViewController.h"
#import"ASCcLevelOnePaperScene.h"
staticLeavesViewController *leavesViewInstance = nil;
@implementation LeavesViewController
@synthesize leavesView;
- (id)init
{
if (self = [superinit])
{
leavesView = [[LeavesViewalloc] initWithFrame:CGRectZero];
leavesView.mode =UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? LeavesViewModeSinglePage : LeavesViewModeFacingPages;
leavesViewInstance = self; //注意这里
}
return self;
}
- (void)dealloc
{
[leavesViewrelease];
leavesViewInstance = nil; //释放全局变量
[superdealloc];
}
+ (id)shareInstance
{
//NSAssert(leavesViewInstance!=nil,@"leavesViewInstance can not be nil!");
if(leavesViewInstance ==nil)
leavesViewInstance = [selfinit];
return leavesViewInstance;
}
这里只展示了“半单例”的实现部分,关于Controller都有的ViewDidLoad等方法和其他相关实现方法,这里与“半单例”无关,不做展示
LeavesView只是一个普通的视图类。
当LeavesView判断到最后一页时:
if([selfhasNextPage]==NO)
{
NSLog(@"最后一页!");
[[LeavesViewControllershareInstance] goToPlay];
}
[LeavesViewControllershareInstance]得到当前ViewController。再发送消息goToPlay,让ViewController响应。