关于单例设计模式的理解

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个对象的指针都是一样的,说明这几个对象都是指向同一个对象,相当于只有第一个对象有效,后面几个对象都无效,说明单例成功

       

        //下面对一个对象经过4release,结果没有报错,说明单例已经成功了,release也被重写了

        [userContex release];

        [userContex release];

        [userContex release];

        [userContex release];

       

        NSLog(@"");

    }

    return 0;

}


2) ARC模式下单例创建:

在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


//ViewController.m

#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






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值