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. SpringMVC3,使用RequestMappint的Param参数,实现一个url绑定多个方法

    SpringMVC中,默认不能把多个相同的url绑定到同一个方法.如果需要绑定,需要增加param参数,而且值要不同. 我自己没有这个需求,或者就是有需求,我也想到的是使用不同的url. 项目中有少部 ...

  2. [redis] redis配置文件redis.conf的详细说明

    # Redis 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写) # # 1k => bytes # 1kb => ...

  3. 轮询方法FileSystemWatcher

    具体实现 FileSystemWatcher watcher = new FileSystemWatcher(WriteBackDir, "*result.txt"); watch ...

  4. 检索 04 --Stack栈 Queue队列 Hashtable哈希表

    //Stack 先进后出 没有索引 Stack st = new Stack(); st.Push(12); st.Push(11); st.Push(22); st.Push(34); st.Pus ...

  5. An HTTP & HTTP/2 client for Android and Java applications OkHttp

    HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP effic ...

  6. 【56.74%】【codeforces 732B】Cormen --- The Best Friend Of a Man

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  7. 【Cocos2d-x Lua】数据库封装类型的操作

    Lua数据库封装类型的操作 使用演示样例 lua代码: require("DB") -- 保存一个字符串(数据库中存储的数据都是以字符串的形式保存的) DB:getInstance ...

  8. 【34.40%】【codeforces 711D】Directed Roads

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  9. Vue.JS学习基础

      = 导航   顶部 vue.js介绍 vue.js实例 模板语法 计算属性 样式绑定 条件渲染 列表渲染 事件处理器 表单控件绑定 组件   顶部 vue.js介绍 vue.js实例 模板语法 计 ...

  10. 分布式缓存技术PK:选择Redis还是Memcached?

    作者:田京昆(腾讯后台研发工程师) 来源:腾云阁(https://www.qcloud.com/community/article/129) Memcached和Redis,作为近些年最常用的缓存服务 ...