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 NDK 的学习之旅-----JNI 数据类型

    经典老套流程,学编程语言东西,先学它最基础的数据类型,JNI也是如此.JNI 定义了一系列基本数据类型和引用数据类型与java想对应. 1.基本数据类型 下面一张表是描述了 Java 基本数据类型和J ...

  2. php自带加密解密函数

    php自带加密解密函数 一.总结 一句话总结:可逆和不可逆函数. 二.php自带加密解密函数 1.不可逆的加密函数为:md5().crypt() md5() 用来计算 MD5 哈稀.语法为:strin ...

  3. PHP怎么读写XML?(四种方法)

    PHP怎么读写XML?(四种方法) 一.总结 1.这四种方法中,字符串的方式是最原始的方法.SimpleXML和DOM扩展是属于基于树的解析器,把整个文档存储为树的数据结构中,需要把整个文档都加载到内 ...

  4. [Angular] @ViewChild and template #refs to get Element Ref

    We can use @ViewChild with component: @ViewChild(AuthMessageComponent) message: AuthMessageComponent ...

  5. 十年磨一剑 Delphi再写传奇(不争辩,不解释,十年坚持不懈的努力)

    新年伊始,英巴卡迪诺公司(Embarcadero)就在其官网发布了“激动人心的RAD Studio2018年发展规划”公告(见上图).公告中指出,将在于2018年发布10.3.X新版本,新版本兼容Ex ...

  6. php取两位小数的几种方法

    php取两位小数的几种方法 一.总结 一句话总结: 1.round   四舍五入 2.sprintf   c语言方式 3.number_format 千分位数字格式化的那个函数 二.php取两位小数的 ...

  7. storm 经常使用类

    弄 <dependency> <groupId>org.apache.storm</groupId> <artifactId>storm-core< ...

  8. 全文检索(elasticsearch入门)

    Elasticsearch篇: Elasticsearch是一个采用java语言开发的,基于Lucene构造的开源,分布式的搜索引擎. 设计用于云计算中,能够达到实时搜索,稳定可靠. Elastics ...

  9. Arcgis api for javascript学习笔记(3.2X版本)-Map图层叠加以及基本操作

    1. 不设置默认底图,第一个图层作为底图,然后叠加另外一个图层 先添加图层1,第一个图层1作为默认底图,然后在图层1上叠加图层2,并设置图层2的透明度为50%. <!DOCTYPE html&g ...

  10. javascript的回调函数 同步 异步

    后一个任务等待前一个任务结束再执行.程序执行顺序与任务排列顺序一致的,同步的. 参考: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%B ...