UML类图:
实例实现代码:
@interface Singleton : NSObject
+ (Singleton *) sharedInstance;
@end
@implementation Singleton
static Singleton * sharedSingleton = nil;
+ (Singleton *) sharedInstance
{
@synchronized(self)//--------1
{
if (sharedSingleton == nil) {
sharedSingleton = [[super allocWithZone:NULL] init];//-----------2
}
}
return sharedSingleton;
}
+ (id) allocWithZone:(struct _NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id) copyWithZone:(NSZone *) zone//----------3
{
return self;
}
- (id) retain
{
return self;
}
- (NSUInteger) retainCount
{
return NSUIntegerMax;//表示不能释放的对象
}
- (void) release
{
//什么也不做
}
- (id) autorelease
{
return self;
}
@end
说明:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1.多线程中多个线程同时调用sharedInstance方法的时候,会有可能创建多个实例,因此我们使用synchronized指令创建同步锁,使一个线程位于代码临界区时,另外一个线程等待。
2.使用super而不使用self是因为self已经重载了allocWithZone:方法,在allocWithZone:方法中,只是返回从sharedInstance方法返回的类实例,在Cocoa Touch框架中,调用类的allocWithZone:方法,会分配实例的内存,引用计数会置为1,然后会返回实例。我们已经见过alloc方法用于很多场合,其实,alloc是用设为NULL的zone来调用allocWithZone:,在默认区域(zone)为新实例分配内存。
3.重载copyWithZone:方法保证不会返回实例的新副本,而是返回self,同一个实例。
重载其他的方法保证他们什么都不做,只是返回self。
子类化Singleton:
首先我们定义一个子类
@interface SingletonSon : Singleton
@property (nonatomic,strong) NSString *name;
@end
@implementation SingletonSon
@end
客户端代码:
Singleton *object = [Singleton sharedInstance];//---------1
SingletonSon *objectSon = [[SingletonSon alloc]init];//-----------2
NSLog(@"object.class = %s",object_getClassName(objectSon));
if(objectSon == object)
{
NSLog(@"同一个实例");
}
objectSon.name = @"SingletonSon";
打印信息:
2015-05-06 15:40:21.380 AppTest[12228:115696] object.class = Singleton
2015-05-06 15:40:21.380 AppTest[12228:115696] 同一个实例
2015-05-06 15:40:21.380 AppTest[12228:115696] -[Singleton setName:]: unrecognized selector sent to instance 0x7fe7e35334d0
PS:
我们上面已经说过alloc是用设为NULL的zone来调用allocWithZone:,在默认区域(zone)为新实例分配内存。
因此,当我们已经创建单例的实例以后,再创建子类的实例,会转发给super,调用super的allocWithZone:方法回到sharedInstance方法中去,因此最后无论怎么创建新的实例都是同一个实例,也就是说,如果我将1和2交换执行顺序,那么我的单例的实例类型就会是SingletonSon而不是Singleton,但是他们依然是同一个实例。
我们要如何子类化呢?
我们可以通过id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)方法通过指定类的类型来实例化任何对象。
第一个参数是要实例化的类的类型;
第二个参数是用于索引的实例变量的额外的字节数,它总是0;
第三个参数用于指定内存中分配的区域,它一般为NULL,表示默认区域。
客户端代码:
Singleton *object = [Singleton sharedInstance];//---------1
SingletonSon *objectSon =[NSAllocateObject([SingletonSon class],0,NULL) init];//------------2
NSLog(@"object.class = %s",object_getClassName(objectSon));
if(objectSon == object)
{
NSLog(@"同一个实例");
}
objectSon.name = @"SingletonSon";
打印信息:
2015-05-06 15:42:17.483 AppTest[12319:116857] object.class = SingletonSon
此时无论1和2的顺序如何都不会是同一个实例变量了。
参考资料
-《大话设计模式》-《Objective-C编程之道》