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

来源:互联网 发布:java 线程池 状态 编辑:程序博客网 时间:2024/05/21 17:53

知识点

  • 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
原创粉丝点击