1. 什么是单例设计模式?
1) 单例类被其他外部类创建访问时,该单例类只能返回一个实例对象。
2) 单例返回对象的值是全局性的,如A和B都引用了C这个单例,但A或者B任意一个类改变了单例类C的值,那么另一个类也跟着改变
3) 如果是普通类,被创建多次时所返回的对象是不一样的,如同是被A和B类所创建,A类和B类中得到该普通类的对象是不一样的。
2. 单例设计模式的优缺点
1) 优点:简单易用,限制一个类只有一个实例,可以减少创建多个对象可能会引起的内存问题的风险,包括内存泄漏、内存占用问题。
2) 缺点:单例模式因为提供了一个全局的访问点,你可以在程序的任何地方轻而易取地访问到,这本身就是一种高耦合的设计。一旦单例改变以后,其它模板都需要修改。另 外,单例模式使得对象变成了全局的了。全局变量是非常邪恶的,要尽量不要使用。而且单例模式会使得对象的内存在程序结束之前一直存在,在一些使用GC的语言里面,这其实就是一种内存泄漏,因为它们永远都不到释放。
3. 什么时候要用到单例设计模式?
1) 当需要共同访问某一个全局对象的时候会用到,如有1个统计唱歌得分的功能,这时可将存储得分这个对象设计成一个单例类,如:A。然后B类引用这个单例类A,用定时器的方式将当前演唱的分数值传给单例A,这个时候如果C也引用了单例A,也可以直接拿到A类传给单例类C的分数值。
2) 有时候我们需要使用一个实用类A,这个类A专门提供一些公共功能供别人调用,而本身并不会处理业务逻辑。由于类A会被许多类乃至线程调用,假设我们的程序非常庞大,在运行的过程中,会访问这个类A100次,为了调用类A的方法,需要先创建A的对象,A a = new A()。这种方法在对A的访问量较少的情况下没问题,但是像我们这种情况,就会创建100个类A的实例,这100个实例是要占用内存的,从这种角度来说,就造成了大量不必要的开销。而单例模式,在整个程序生命周期中,只有一个实例,这样就不会造成不必要的内存消耗
4. 如何设计单例模式?
1) 传统设计方式
在objective-c中要实现一个单例类,至少需要做以下四个步骤:
1、为单例对象实现一个静态实例,并初始化,然后设置成nil,
2、实现一个实例构造方法检查上面声明的静态实例是否为nil,如果是则新建并返回一个本类的实例,
3、重写allocWithZone方法,用来保证其他人直接使用alloc和init试图获得一个新实力的时候不产生一个新实例,
4、适当实现allocWitheZone,copyWithZone,release和autorelease。
下面以SurveyRunTimeData为例子:
#import <Foundation/Foundation.h>
@interface UserContext : NSObject<NSCopying> //单例也必须继承与NSCopying
{
}
@property (nonatomic,copy)NSString *name;
@property (nonatomic,copy)NSString *email;
+ (id) shareUserContext; //声明单例的类方法,一般开头命名为:share或者default
@end
UserContext.m
#import "UserContext.h"
static UserContext *segtonInstance= nil; //第一步:静态实例,并初始化。
@implementationUserContext
+ (id) shareUserContext
{
//@synchronized是多线程锁,目的是为了保护只让一个对象来访问,如A对象访问了,就允许B对象来访问,防止多线程去修改同一个实例
@synchronized(self){
if (segtonInstance ==nil) { //第二步:实例构造检查静态实例是否为nil
segtonInstance = [[[selfclass ]alloc]init];
}
}
return segtonInstance;
}
//下面方法为了确保只有一个实例对象,以后写项目的时候可以复用的,就是通用的写法
+(id)allocWithZone:(NSZone *)zone//第三步:重写allocWithZone方法
{
if (segtonInstance ==nil) {
segtonInstance = [superallocWithZone:zone];
}
return segtonInstance;
}
- (id)copyWithZone:(NSZone *)zone//第四步
{
return segtonInstance;
}
- (id)retain
{
return segtonInstance;
}
- (oneway void) release
{
}
- (id) autorelease
{
return segtonInstance;
}
- (NSUInteger) retainCount
{
return UINT_MAX; //返回一个最大值,UINT_MAX是库里的属性值
}
@end
main.m
#import <Foundation/Foundation.h>
#import "UserContext.h"
int main(int argc,constchar * argv[])
{
@autoreleasepool {
UserContext *userContex = [UserContextshareUserContext];
UserContext *userContex2 = [UserContextshareUserContext];
UserContext *userContex3 =[[UserContextalloc]init];
UserContext *userContex4 =[userContexcopy];
//以上4个对象的指针都是一样的,说明这几个对象都是指向同一个对象,相当于只有第一个对象有效,后面几个对象都无效,说明单例成功
//下面对一个对象经过4次release,结果没有报错,说明单例已经成功了,release也被重写了
[userContex release];
[userContex release];
[userContex release];
[userContex release];
NSLog(@"");
}
return 0;
}
2) ARC模式下单例创建:
在.m中保留一个全局的static的实例 static id _instance;
1)重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
+ (id)allocWithZone:(struct _NSZone *)zone
{ @synchronized(self) {
if (!_instance) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
2)提供1个类方法让外界访问唯一的实例
+ (instancetype)sharedSoundTool
{
@synchronized(self) {
if (!_instance) {
_instance = [[self alloc] init];
}
}
return _instance;
}
3)实现copyWithZone:方法
+ (id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
2) 通过dispatch
创建单例有更加简单的方式了,使用GCD,我们可以这样重写上面的方法,使用函数 dispatch_once:
+ (id)sharedWhatever
{
static dispatch_once_t pred;
static Whatever *whatever = nil;
dispatch_once(&pred, ^{
whatever = [[Whatever alloc] init];
});
return whatever;
}
该方法有很多优势:
1 线程安全
2 很好满足静态分析器要求
3 和自动引用计数(ARC)兼容
4 仅需要少量代码
这个稍微比 @synchronized方法简单些,并且GCD确保以更快的方式完成这些检测,它保证block中的代码在任何线程通过 dispatch_once 调用之前被执行,但它不会强制每次调用这个函数都让代码进行同步控制。实际上,如果你去看这个函数所在的头文件,你会发现目前它的实现其实是一个宏,进行了内联的初始化测试,这意味着通常情况下,你不用付出函数调用的负载代价,并且会有更少的同步控制负载。
如下实例中包含的类文件:
SharedInstance //单例类,存储1个全局变量
SetUserName //为单例类SharedInstance的name属性负值
ShowUserName //取出单例类中name属性的值
ViewController //用来实例化单例类的
//SharedInstance.h
#import <Foundation/Foundation.h>
@interface SharedInstance : NSObject
@property (nonatomic,copy)NSString *name;
+ (SharedInstance *)sharedUserInfo;
@end
//SharedInstance.m
#import "SharedInstance.h"
@implementation SharedInstance
+ (SharedInstance *)sharedUserInfo
{
static SharedInstance *sharedInstance = nil;
static dispatch_once_t predicate;
if (!sharedInstance){
dispatch_once(&predicate, ^{
sharedInstance = [[SharedInstance alloc]init];
});
}
return sharedInstance;
}
@end
//SetUserName.h
#import <Foundation/Foundation.h>
@interface SetUserName : NSObject
- (void)printName;
@end
//SetUserName.m
#import "SetUserName.h"
#import "SharedInstance.h"
@implementation SetUserName
- (void)printName
{
SharedInstance *userInfo = [SharedInstance sharedUserInfo];
userInfo.name = @"mayday";
NSLog(@"userInfo:%@",userInfo.name);
}
@end
//ShowUserName.h
#import <Foundation/Foundation.h>
@interface ShowUserName : NSObject
- (void)outPrintUserInfo;
@end
//ShowUserName.m
#import "ShowUserName.h"
#import "SharedInstance.h"
@implementation ShowUserName
- (void)outPrintUserInfo
{
SharedInstance *userInfo = [SharedInstance sharedUserInfo];
NSLog(@"name:%@",userInfo.name);
}
@end
#import "ViewController.h"
#import "ShowUserName.h"
#import "SetUserName.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
SetUserName *setUserName = [[SetUserName alloc]init];
[setUserName printName];
ShowUserName *showUserName = [[ShowUserName alloc]init];
[showUserName outPrintUserInfo];
}
@end