转载请出名出处:http://my.oschina.net/liuchuanfeng/blog/660917
以下为翻译部分:
翻译自:http://www.galloway.me.uk/tutorials/singleton-classes/ 若有不精确之处,还望不吝赐教。
单例模式singleton pattern是经常使用的设计模式,它不用传递参数,就能在两个不同的代码块之间实现数据共享
比如:从任何地方调用UIApplication 的 sharedApplication方法,都会返回与当前运行程序相关的UIApplication实例。
如何实现单例类:(ARC)
#import <foundation/Foundation.h> @interface MyManager : NSObject { NSString *someProperty; } @property (nonatomic, retain) NSString *someProperty; + (id)sharedManager; @end
#import "MyManager.h" @implementation MyManager @synthesize someProperty; #pragma mark Singleton Methods + (id)sharedManager { static MyManager *sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] init]; }); return sharedMyManager; } - (id)init { if (self = [super init]) { someProperty = [[NSString alloc] initWithString:@"Default Property Value"]; } return self; } - (void)dealloc { // Should never be called, but just here for clarity really. } @end
上面定义了静态变量sharedMyManager 在sharedManager里只被实例化一次,GCD的dispatch_once方法保证只被创建一次,并且是线程安全的。
当然,如果不想使用GCD,可以使用下面的代码:
+ (id)sharedManager { static MyManager *sharedMyManager = nil; @synchronized(self) { if (sharedMyManager == nil) sharedMyManager = [[self alloc] init]; } return sharedMyManager; }
用下面的方法可以从任何一个地方调用这个单例
MyManager *sharedManager = [MyManager sharedManager];
单例使用广泛,比如处理CoreLocation or CoreData函数
非ARC代码:
#import "MyManager.h" static MyManager *sharedMyManager = nil; @implementation MyManager @synthesize someProperty; #pragma mark Singleton Methods + (id)sharedManager { @synchronized(self) { if(sharedMyManager == nil) sharedMyManager = [[super allocWithZone:NULL] init]; } return sharedMyManager; } + (id)allocWithZone:(NSZone *)zone { return [[self sharedManager] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be released } - (oneway void)release { // never release } - (id)autorelease { return self; } - (id)init { if (self = [super init]) { someProperty = [[NSString alloc] initWithString:@"Default Property Value"]; } return self; } - (void)dealloc { // Should never be called, but just here for clarity really. [someProperty release]; [super dealloc]; } @end
以下加入我的理解:
不知你是否在上面ARC下用如下代码测试过
MyManager *mm1 = [[MyManager alloc] init]; MyManager *mm2 = [[MyManager alloc] init]; MyManager *mm3 = [[MyManager alloc] init]; NSLog(@"\n%@\n%@\n%@",mm1,mm2,mm3); MyManager *mm4 = [MyManager sharedManager]; MyManager *mm5 = [MyManager sharedManager]; NSLog(@"\n%@\n%@",mm4,mm5);
mm1 mm2 mm3的值(地址)是不一样的,mm4 mm5一样,重写allocWithZone就能保证mm1 mm2 mm3的值一样了。当然如果需要copy,则要重写copyWithZone
+(id)allocWithZone:(NSZone *)zone{ @synchronized(self){ if (sharedMyManager == nil) { sharedMyManager = [super allocWithZone:zone]; //确保使用同一块内存地址 return sharedMyManager; } return sharedMyManager; } } - (id)copyWithZone:(NSZone *)zone { return self; }
为什么实现allocWithZone、copyWithZone这两个方法?
创建对象的时候,alloc表示申请内存,init表示初始化,程序在alloc时,会在allocWithZone这个方法申请内存,我们只要在这个方法中调用sharedManager返回单例即可。这样就不会申请多次内存了。拷贝对象以此类推,同理所得,要重写copyWithZone。这样就保证了这个类只被实例化了一次。
@synchronized 保证线程安全
单例可以用来传值,系统UIApplication NSUserDefaults 就是单例类。
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例