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. 【u024】没有上司的舞会

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就 ...

  2. JS null问题

    在学习getElementById()方法的过程中出现了这样一个问题,便想记录下来. 分析问题之前,我们最好还是先来认识一下getElementById()方法.getElementById()方法, ...

  3. js如何操作表格(常用属性方法汇总)

    js如何操作表格(常用属性方法汇总) 一.总结 一句话总结: 二.表格相关的属性和方法 1.1 Table 对象集合 cells[] 返回包含表格中所有单元格的一个数组. 语法:tableObject ...

  4. Centos root权限的变化

    方法一:更改 /etc/sudoers 文件, 找到以下一行,把前面的凝视(#)去掉 ## Allows people in group wheel to run all commands %whee ...

  5. 【cordova】cordova安装步骤(windows)

    原文:[cordova]cordova安装步骤(windows) 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/snow_finland/artic ...

  6. nodebb中文社区

    V2MM —— 自由职业者社区 https://v2mm.tech/ 萌梦社区 https://qtdream.com/ React Native 中文社区 http://bbs.reactnativ ...

  7. tomcat添加跨域访问功能-Access-Control-Allow-Origin:*

    目标:tomcat服务器提供的接口,不能在其他域中访问的时候,需要增 Access-Control-Allow-Origin:* 直接配置的方法很多,但是我一个没有成功过,所以只能自己写拦截器了. 1 ...

  8. Android 如何检索Android设备的唯一ID

    关于本文档 Android的开发者在一些特定情况下都需要知道手机中的唯一设备ID.例如,跟踪应用程序的安装,生成用于复制保护的DRM时需要使用设备的唯一ID.在本文档结尾处提供了作为参考的示例代码片段 ...

  9. wpf控件开发基础(2) -属性系统(1)

    原文:wpf控件开发基础(2) -属性系统(1) 距离上篇写的时间有1年多了.wpf太大,写的东西实在太多,我将依然围绕着自定义控件来展开与其相关的技术点. 也欢迎大家参与讨论.这篇我们将要讨论的是W ...

  10. javascript相框echarts插件实现酷立方效果图的人

    最近由于项目需求,我们需要做的一类似网络效应的人立方效果,很多文件中的查询之后.百度发现echarts开源组件非常适合,而加载速度是伟大的.echarts图形主要使用html5这些新功能做,使用can ...