iOS单例模式的实现

首先我们要明白下面三个问题:

  1. 什么是单例模式
  2. 单例模式的优点
  3. 如何实现单例模式

1.什么是单例模式

单例模式(Singleton):单例模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局唯一的访问点。

2.单例模式的优点

  1. 节省内存开销:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。
  2. 如果有一些数据,整个程序都用得上,使用同一份资源即可。(保证大家访问的数据是相同的,一致的)

  例如:[NSUserDefaults standardUserDefaults],[UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager]等,所有的这些方法都返回一个单例对象,苹果公司大量使用了此模式。

3.如何实现单例模式

  一般情况下,项目中的工具类使用单例模式比较合适,工具类一般是整个项目都用的上,但是只用一个,所以没必要创建多个。

  单例的实现应该分为两种情况:非ARC(MRC)和ARC

3.1MRC下单例模式的实现

  MRC情况下我们的单例模式实现如下:

    1. 需要把我们的项目配置成MRC
    2. 新建一个网络工具类

  1>   在不使用单例模式的情况下打印三个实例对象,他们指向的地址是不一样的。示例代码如下:

  DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];

  DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];

   DHNetworkTool *tool3 = [[DHNetworkTool alloc]init];

    NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);

  2>    我们希望通过DHNetworkTool创建的对象是同一个,也就是只分配一块内存空间,那我们应该去重写alloc方法,因为alloc方法负责分配内存空间的。

  + (instancetype)alloc {}      + (instancetype)allocWithZone:(struct _NSZone *)zone {}

  现在发现有以上两个方法,那么我们应该重写哪个alloc方法呢?我的建议是重写后者也就是+ (instancetype)allocWithZone:(struct _NSZone *)zone {}

为什么?

因为alloc内部会调用allocWithZone,也就是说allocWithZone方法更底层。也就是说我们实现alloc方法的话就只能拦截alloc方法,但是实现allocWithZone方法的话,任何内存分配的方法我们都能拦截。

Zone的意思就是空间,当你调用这个方法的时候,系统会自动给你传递一块内存空间,Zone就是系统分配给开发者APP的内存空间。

  注意:在我们实现allocWithZone能调用父类的方法吗?不能!如果调用了就相当于我们没写!!!我们的目的就是不要使用父类的,我们自己做自己的事情。

  3>    我们需要创建一个全局变量为了不让别人访问,我们应该使用static修饰一下

 //用static是为了不让别人访问这个变量
static DHNetworkTool *_networkTool = nil; //alloc内部会调用allocWithZone,我们实现alloc方法的话就只能拦截alloc方法,但是实现allocWithZone方法的话,任何内存分配的方法我们都能拦截
+ (instancetype)allocWithZone:(struct _NSZone *)zone { //如果在这里调用父类的方法相当于没有重写allocWithZone方法
// return [super allocWithZone:zone]; //这样判断的话就能保证我们返回的都是_networkTool这个对象了
if (_networkTool == nil) { static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{//保证线程安全,而且这个代码只会被执行一次 //在这里可以调用父类的方法了
_networkTool = [super allocWithZone:zone];
}); }
return _networkTool;
}

4>   我们初步实现后可以再次验证是否创建的对象是同一个

打印结果可以验证是同一个对象,因为实例化对象的时候要调用allocWithZone方法,在该方法实例化对象的代码只会走一次,所以保证每次实例化的对象都是同一个。

  5>   在MRC环境下我们这样写是不严谨的,因为还可能会调用release方法!如果调用了release方法那就意味着这个对象完了,下次在调用alloc方法的时候就没办法创建对象了,因为在allocWithZone方法中实例化对象的代码只走一次。

  为了避免这种情况,我们还需要重写release方法,拦截对象被释放。重写release方法就是为了保证整个程序都有这个单例对象。

//重写release防止对象被释放,因为对象一旦被释放就再也不能生成了。
- (oneway void)release { }

  6>   除了release方法外,我们还需要重写retain方法。为什么呢?因为我们是单例模式,这个对象只会被创建一次,那么我们就一直让他的引用计数为1,不要增加,不要让其增加。那么我们还需要再重写retainCount方法,返回1就好了。

 //使单例对象引用计数不增加
-(instancetype)retain { return self;
} //使单例对象引用计数一致为1
- (NSUInteger)retainCount { return ;
}

  7>    allocWithZone release retainCount三者不可缺一。

  8>    单例模式已经基本实现了,最后一步就是我们应该仿照系统实现一个类方法返回我们的单例对象。

注意:如果直接返回对象的话,那么这个对象就会一直为空,所以需要在类方法里调用alloc ini方法。但是每次都调用init方法的话,我们的对象每次都要被初始化,所以要重写init方法,保证这个单例对象只执行一次初始化。为什么不判断对象为空呢?因为调用init的时候要先执行alloc方法。

 + (instancetype)sharedNetworkTool {

     //直接返回这个对象意味着它一直为空
// return _networkTool;
return [[self alloc] init];
} //每次都调用init方法的话,我们的对象每次都要被初始化,所以要保证init只执行一次
-(instancetype)init { static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_networkTool = [super init];
});
return _networkTool;
}

最后再次验证一下,打印结果如下:

DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];

DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];

DHNetworkTool *tool3 = [DHNetworkTool sharedNetworkTool];

NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);

3.2 ARC下单例模式的实现

  ARC下单例模式的实现相对比较简单,下面就只是展示.m文件部分源码,不做赘述了。

 static id _instance = nil;

 + (id)allocWithZone:(struct _NSZone *)zone
{
if (_instance == nil) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 安全(这个代码只会被调用一次)
_instance = [super allocWithZone:zone];
});
}
return _instance;
} - (id)init
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super init];
});
return _instance;
} + (instancetype)sharedDataTool
{
return [[self alloc] init];
}

  最后一点,在公司项目开发过程中我们通常会把单例模式的实现抽取成一个宏,放到.PCH文件中,这样方便项目组中的每个人去使用,再次也做一下抽取和代码的展示吧,可以直接拿到工程中使用。

 //实现单例设计模式

 // .h文件的实现
#define SingletonH(methodName) + (instancetype)shared##methodName; // .m文件的实现
#if __has_feature(objc_arc) // 是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} #else // 不是ARC #define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return ; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} #endif

  写了一个小小的Demo做简单的展示,欢迎大家一起学习交流哇。链接: http://pan.baidu.com/s/1bpHjYUF 密码: ti7y

iOS学习总结之ARC和非ARC的单例模式实现的更多相关文章

  1. ios工程中ARC与非ARC的混合

    ARC与非ARC在一个项目中同时使用, 1,选择项目中的Targets,选中你所要操作的Target,2,选Build Phases,在其中Complie Sources中选择需要ARC的文件双击,并 ...

  2. iOS: ARC和非ARC下使用Block属性的问题

    1. Block的声明和线程安全 Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非AR ...

  3. (转)iOS 开发,工程中混合使用 ARC 和非ARC

    [前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...

  4. iOS 开发,工程中混合使用 ARC 和非ARC(转)

    [前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...

  5. iOS arc和非arc 适用 宏

    iOS arc和非arc 适用 宏 1:使用宏 + (void)showAlertWithMessage:(NSString *)messages { dispatch_async(dispatch_ ...

  6. iOS 开发,混合使用 ARC 和非ARC

    [前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...

  7. IOS开发 ARC和非ARC下使用Block属性的问题

    1. Block的声明和线程安全 Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非AR ...

  8. IOS ARC和非ARC文件混用

    ARC在SDK4.0的时候增加的,因为要和曾经的项目融合,就会有arc和非arc文件的混合. 当然,也就这两种情况: 1.自己的旧项目没有使用ARC,可是引入的第三方库却是使用了ARC的. 2.自己的 ...

  9. iOS中ARC和非ARC混用

    如果在使用第三方类库的时候,我们可能会遇到一些内存管理的问题   那么如何在一个工程中实现ARC和非ARC混用呢,例如你创建一个ARC的工程,但是你引用的第三方类库是非ARC管理内存的   首先点击工 ...

  10. ARC简介以及工程中ARC与非ARC的混合

    Piosa 博客园 博问 闪存 首页 新随笔 联系 管理 订阅 随笔- 79  文章- 0  评论- 13    ARC简介以及工程中ARC与非ARC的混合   ARC与非ARC在一个项目中同时使用, ...

随机推荐

  1. Android 长按事件和短按事件同时响应

    在长按事件中的return false 改为return true就可以解决这个问题

  2. 温故而知新-String类

    String不算是一种类型,而算是一个类.就是说String不仅能够表示string类型,另一些自带的方法能够调用.温故而知新.如今给大家总结了String类应该注意的地方. (1)"==& ...

  3. 树莓派——root用户和sudo

    Linux操作系统是一个多用户操作系统,它同意多个用户登录和使用一台计算机. 为了保护计算机(和其它用户的隐私).用户都被限制了能做的事情. 大多数用户都同意执行计算机上大部分程序,而且编辑和保存存放 ...

  4. WM_NOTIFY消息流程实例分析

    我们以CListCtrl控件为例来分析WM_NOTIFY消息. CListCtrl控件在Report样式下会包含CHeaderCtrl标头控件,即CHeaderCtrl标头控件为CListCtrl控件 ...

  5. Qt 元对象系统(Meta-Object System)(不管是否使用信号槽,都推荐使用)

    Qt 元对象系统(Meta-Object System) Qt的元对象系统基于如下三件事情: 类:QObject,为所有需要利用原对象系统的对象提供了一个基类. 宏:Q_OBJECT,通常可以声明在类 ...

  6. react中对象扩展运算符使用问题

    看别人代码看到 render() { return <Modal {...this.props}> {this.props.children} </Modal> } 对其中的{ ...

  7. Android开发之assets目录下资源使用总结

    预前知识: Android资源文件分类: Android资源文件大致可以分为两种: 第一种是res目录下存放的可编译的资源文件: 这种资源文件系统会在R.Java里面自动生成该资源文件的ID,所以访问 ...

  8. mybatis在CRUD

    一. 一个简短的引论: Mybatis本是apache的一个开源项目ibatis, 2010年这个项目由apache software foundation迁移到了google code, 而且改名为 ...

  9. 利用伪元素before实现自定义checkbox样式

     原理:label标签的for属性可以将文字与复选框进行关联,点击文字可以同时将复选框选中. 隐藏checkbox标签,利用label标签的before伪元素来实现自定义样式.设置before伪元素的 ...

  10. Windows Container 和 Docker

    Windows Container 和 Docker 微软在2016年的Ignite技术大会上正式发布了Windows Server 2016,其中的容器服务已经可以作为生产环境使用.这意味着Wind ...