创建"全世界最简单"の单例

知识点

  • 1、什么是单例
  • 2、单例的好处
  • 3、单例创建方式
  • 4、封装”单例的创建”
  • 5、使用单例的封装实现单例
  • 6、使用单例

1、What’s 单例 ?

顾名思义,单例就是一个类只有一个实例对象。确保在程序过程中,无论创建多少次对象,该对象都是同一个实例,都指向同一块存储空空间。在设计模式中单例模式也是很常用的模式。

单例模式的三个要点
  • 该类只能有一个实例;
  • 它必须自行创建这个实例;
  • 必须向外界提供这个实例供调用者调访问。

2、Benefit of 单例

  • 可以保证在程序运行过程中,一个类只有一个实例,而且该实例易于供外界访问
  • 方便的控制了实例个数,并且大大节约系统资源

单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次)


3、The ways to create 单例

创建方式

  • a) 利用手动添加线程锁@synchronized控制
    首先要在类中申明一个私有的静态实例,保证该类被循环引用,这样单例才会一直存在不会被释放。

    static id _instance;

    要想实例单例,就必须控制类的构造方法,因此重写allocWithZone方法。

    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        @synchronized (self) {
            if(!_instance) {
            _instance = [super allocWithZone:zone];
            }
        }
    return _instance;
    }

    同理,要达到实现单例,按照苹果提供的[UIApplication sharedApplication]方式获取单例,需要提供一个sharedInstance的类方法去访问单例。因此需要在类的.h文件中声明工厂方法,并在.m文件中实现。

    + (instancetype)sharedInstance;
    + (instancetype)sharedCar {
    @synchronized (self) {
        if(!_instance) {
            _instance = [[self alloc] init];      
        }
    }  
    return _instance;
    }

    当然,有时候我们会用到一个实现了NSCopyingcopy对象方法,在单例中应该重写该方法,使得赋值的实例仍然是唯一的实例。

    - (id)copyWithZone:(NSZone *)zone {
          return _instance;
    }
    

  • b) 通过利用GCD中的dispatch_once方法控制实例个数
    和用线程同步锁一样的道理,要想达到单例的效果,必须控制外界创建实例的方法,而GCD的方式也不例外,只是在控制的方式上利用用dispatch_once程序运行过程中只会执行一次的特性实现,并且dispatch_once方法是线程安全的。

        static id _instance;    
    
            + (instancetype)allocWithZone:(struct _NSZone *)zone {
                 static dispatch_once_t onceToken;
                dispatch_once(&onceToken, ^{
                    _instance = [super allocWithZone:zone];
                });
                return _instance;
            }
    
        + (instancetype)shared##name {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                _instance = [[self alloc] init];
            });
            return _instance;
        }
    
        - (id)copyWithZone:(NSZone *)zone {
            return _instance;
        }

4、封装”单例的创建”

从上面的代码我们可以看出,要想实现一个单例是很容易的。同时也可以看出实现单例的代码都是差不多一样的,除了可能在类的工厂方法的名字可能采用instance***以为,其余的代码都是一模一样,而这样的代码重复工作量,在我们进行开发的过程中是没有多大意思并且花费时间,因此可以借用宏的特性将单例的实现进行封装,只要每次在.h文件中引入宏,并且调用宏的方法就可以实现单例。那么我不啰嗦了,直接上代码。
1. 首先需要创建一个.h文件MRSingleton.h(当然该文件名可以根据个人喜好取名),在.h文件中进行宏的定义。

        // .h 文件中需要声明的方法宏定义
            #define MRSingletonH(name) + (instancetype)shared##name;

            // .m 文件中需要的方法宏定义
            #define MRSingletonM(name) \
            static id _instance;\
            \
            + (instancetype)allocWithZone:(struct _NSZone *)zone {\
            \
            static dispatch_once_t onceToken;\
            \
            dispatch_once(&onceToken, ^{\
            \
            _instance = [super allocWithZone:zone];\
            \
            });\
            \
           return _instance;\
           }\
           \
           + (instancetype)shared##name {\
           \
           static dispatch_once_t onceToken;\
           \
           dispatch_once(&onceToken, ^{\
           \
           _instance = [[self alloc] init];\
           \
           });\
           \
          return _instance;\
          }\
          \
          - (id)copyWithZone:(NSZone *)zone {\
          \
          return _instance;\
          }

注意!!!
需要特别注意的是上面的宏定义中的\是一定不能获缺的,因为编译器在检测宏时默认只会认为define所在一行才是宏的定义内容,因此只会将所在行的代码进行宏的替换,\的的作用就是告诉编译器后面一行的内容仍然是宏需要替换的代码。


5、使用单例的封装实现单例

  • 1 在声明文件.h文件中导入宏定义文件MRSingleton.h

    
    #impport "MRSingleton.h"
    
  • 2 在.h文件中调用宏的方法MRSingletonH(类名)(在MRSingletonH后括号中填入类名, 生成方法instanceMRPerson的方法声明)
    @interface MRPerson : NSObject

    // 调用宏声明单例方法
    MRSingletonH(Person)

    @end
  • 3 在.h 文件中调用宏的方法MRSingletonH(类名)
    @interface MRPerson ()<NSCopying>
    @end

    @implementation MRPerson

    // 调用宏实现单例方法 
    MRSingletonM(MRPerson)

    @end

6、单例使用

#pragma mark --- 利用GCD实现单例
    // 原始方法创建
    MRPerson *person2 = [[MRPerson alloc] init];
    // 单例方法创建
    MRPerson *person1 = [MRPerson sharedPerson];
    // copy
    MRPerson *person3 = [person2 copy];

    NSLog(@"[MRPerson sharedPerson]---%@", person1);
    NSLog(@"[[MRPerson alloc] init]---%@", person2);
    NSLog(@"[person2 copy]---%@", person3);

    ViewController *viewController1 = [ViewController sharedViewController];
    ViewController *viewController2 = [[ViewController alloc] init];
    ViewController *viewController3 = [viewController2 copy];

    NSLog(@"[ViewController sharedViewController]---%@", viewController1);
    NSLog(@"[[ViewController alloc] init]---%@", viewController2);
    NSLog(@"[viewController2 copy]---%@", viewController3);

2016-06-26 20:26:10.903 单例模式-OC[3818:214382] [MRPerson sharedPerson]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [[MRPerson alloc] init]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [person2 copy]---<MRPerson: 0x7bf2cdb0>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [ViewController sharedViewController]---<ViewController: 0x7bf29c40>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [[ViewController alloc] init]---<ViewController: 0x7bf29c40>
2016-06-26 20:26:10.904 单例模式-OC[3818:214382] [viewController2 copy]---<ViewController: 0x7bf29c40>

可以看出单例已经成功创建,每一个实例对象都指向同一块内存空间!!!(ps:这篇是OC版,后续会用swift实现)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值