单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,并提供一个访问它的全剧访问点。
系统只需要一个实例对象,客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。比较典型的例子是音乐播放器,日志系统类等等。
单例模式只有一个成员,就是单例类。因为只有一个成员,所以该设计模式的类图比较简单:
单例模式类图
一般来说单例类会给外部提供一个获取单例对象的方法,内部会用静态对象的方式保存这个对象。
在这里我们创建一个简单的打印日至或上报日至的日至管理单例。
在创建单例时,除了要保证提供唯一实例对象以外,还需注意多线程的问题。下面用代码来看一下。
创建单例类 LogManager
//================== LogManager.h ==================
@interface LogManager : NSObject
+(instancetype)sharedInstance;
- (void)printLog:(NSString *)logMessage;
- (void)uploadLog:(NSString *)logMessage;
@end
//================== LogManager.m ==================
@implementation LogManager
static LogManager* _sharedInstance = nil;
+(instancetype)sharedInstance
{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
_sharedInstance = [[super allocWithZone:NULL] init] ;
}) ;
return _sharedInstance ;
}
+(id)allocWithZone:(struct _NSZone *)zone
{
return [LogManager sharedInstance] ;
}
-(id)copyWithZone:(struct _NSZone *)zone
{
return [LogManager sharedInstance];
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return [LogManager sharedInstance];
}
- (void)printLog:(NSString *)logMessage{
//print logMessage
}
- (void)uploadLog:(NSString *)logMessage{
//upload logMessage
}
@end
从上面的代码中可以看到:
sharedInstance
方法是向外部提供的获取唯一的实例对象的方法,也是该类中的其他可以创建对象的方法的都调用的方法。在这个方法内部使用了dispatch_once
函数来避免多线程访问导致创建多个实例的情况。alloc init
出初始化方法可以返回同一个实例对象,在allocWithZone:
方法里面仍然调用了sharedInstance
方法。copy
和mutableCopy
方法也可以返回同一个实例对象,在copyWithZone:
与mutableCopyWithZone
也是调用了sharedInstance
方法。下面分别用这些接口来验证一下实例的唯一性:
//================== Using by client ==================
//alloc&init
LogManager *manager0 = [[LogManager alloc] init];
//sharedInstance
LogManager *manager1 = [LogManager sharedInstance];
//copy
LogManager *manager2 = [manager0 copy];
//mutableCopy
LogManager *manager3 = [manager1 mutableCopy];
NSLog(@"\nalloc&init: %p\nsharedInstance: %p\ncopy: %p\nmutableCopy: %p",manager0,manager1,manager2,manager3);
我们看一下打印出来的四个指针所指向对象的地址:
alloc&init: 0x60000000f7e0
sharedInstance: 0x60000000f7e0
copy: 0x60000000f7e0
mutableCopy: 0x60000000f7e0
可以看出打印出来的地址都相同,说明都是同一对象,证明了实现方法的正确性。
下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:
单例模式代码示例类图
NSUserDefaults
(key-value持久化)和UIApplication
类(代表应用程序,可以处理一些点击事件等)。Runtime
类(代表应用程序的运行环境,使应用程序能够与其运行的环境相连接);Desktop
类(允许 Java 应用程序启动已在本机桌面上注册的关联应用程序)