有两种方法来创建单例,下面分别介绍

1、GCD方式创建单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static  id _instance;   
+ (instancetype)allocWithZone:( struct  _NSZone *)zone  
{   
     static  dispatch_once_t onceToken;   
     dispatch_once(&onceToken, ^{   
         _instance = [super allocWithZone:zone];   
     });   
     return  _instance;   
}   
+ (instancetype)sharedInstance  
{   
     static  dispatch_once_t onceToken;   
     dispatch_once(&onceToken, ^{   
         _instance = [[self alloc] init];   
     });   
     return  _instance;   
}   
- (id)copyWithZone:(NSZone *)zone   
{   
     return  _instance;   
}  
- (id)mutableCopyWithZone:(NSZone *)zone {   
     return  _instance;   
}

2、互斥锁方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static  id _instance;
+ (instancetype)allocWithZone:( struct  _NSZone *)zone
{    @synchronized(self) {         if  (_instance == nil) {
             _instance = [super allocWithZone:zone];
         }
     }     return  _instance;
}
+ (instancetype)sharedInstance
{    @synchronized(self) {         if  (_instance == nil) {
             _instance = [[self alloc] init];
         }
     }     return  _instance;
}
- (id)copyWithZone:(NSZone *)zone
{     return  _instance;
}

上面两种方式都可以创建单例,而且保证了用户不管是通过shareInstance方法,还是alloc、copy方法得到的实例都是一样的。

上面代码的关键之处就在于如何在多线程情况下保证创建的单例还是同一个。

我们先看看在GCD情况下,如果不使用dispatch_once和同步锁创建单例会出现什么问题,去掉两者后创建单例的代码如下

1
2
3
4
5
6
+ (instancetype)sharedInstance  
{   
  if  (_instance == nil) {
      _instance = [[self alloc] init];
   }
}

假设此时有两条线程:线程1和线程2,都在调用shareInstance方法来创建单例,那么线程1运行到if (_instance == nil)出发现_instance = nil,那么就会初始化一个_instance,假设此时线程2也运行到if的判断处了,此时线程1还没有创建完成实例_instance,所以此时_instance = nil还是成立的,那么线程2又会创建一个_instace。

此时就创建了两个实例对象,导致问题。

解决办法1、使用dispatch_once

dispatch_once保证程序在运行过程中只会被运行一次,那么假设此时线程1先执行shareInstance方法,创建了一个实例对象,线程2就不会再去执行dispatch_once的代码了。从而保证了只会创建一个实例对象。

解决办法2、使用互斥锁

假设此时线程1在执行shareInstance方法,那么synchronize大括号内创建单例的代码,如下所示:

1
2
3
if  (_instance == nil) {
             _instance = [[self alloc] init];
         }

就会被当做一个任务被加上了一把锁。此时假设线程2也想执行shareInstance方法创建单例,但是看到了线程1加的互斥锁,就会进入睡眠模式。等到线程1执行完毕,才会被唤醒,然后去执行上面所示的创建单例的代码,但是此时_instance !=nil,所以不会再创建新的实例对象了。从而保证只会创建一个实例对象。

但是互斥锁会影响性能,所以最好还是使用GCD方式创建单例。


宏创建单例

如果我们需要在程序中创建多个单例,那么需要在每个类中都写上一次上述代码,非常繁琐。

我们可以使用宏来封装单例的创建,这样任何类需要创建单例,只需要一行代码就搞定了。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Singleton.h文件
==================================
#define SingletonH(name) + (instancetype)shared##name;
#define SingletonM(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; \
}\
\
- (id)mutableCopyWithZone:(NSZone *)zone { \
     return  _instance; \
}

如何调用

假设我们要在类viewcontroller中使用,调用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
viewcontroller.h文件
===========================
#import <uikit uikit.h="">
#import "Singleton.h"
@interface ViewController : UIViewController
SingletonH(viewController)
@end
viewcontroller.m文件
===========================
@interface ViewController ()
@end
@implementation ViewController
SingletonM(ViewController)
- ( void )viewDidLoad {
     [super viewDidLoad];
     NSLog(@ "%@ %@ %@ %@" , [ViewController sharedViewController],[ViewController sharedViewController], [[ViewController alloc] init],[[ViewController alloc] init]);
}
@end</uikit>

输出结果

1
  <viewcontroller: 0x7f897061bc00> <viewcontroller: 0x7f897061bc00> <viewcontroller: 0x7f897061bc00> <viewcontroller: 0x7f897061bc00></viewcontroller: 0x7f897061bc00></viewcontroller: 0x7f897061bc00></viewcontroller: 0x7f897061bc00></viewcontroller: 0x7f897061bc00>

可以看到四个对象的内存地址完全一样,说明是同一个对象

iOS 实用方法创建单利+宏定义创建单利相关推荐

  1. iOS日常工作之常用宏定义大全

    前言: 在工作中, 很多小伙伴都会在PCH文件定义一些常用的宏,但是又怕写这些简单的宏浪费时间,又有时候忘记怎么定义了怎么办?本人在工作中也是如此.所以在这里给大家分享一些常用的宏定义,喜欢的小伙伴可 ...

  2. iOS 开发 高级:使用 宏定义macros (#,##,...,__VA_ARGS_)

    一直以来用宏定义#define也就是定义一些简单的常量,至多也就是定义一个函数,很少关注宏定义的用法.直到看到这样的代码: [cpp] view plaincopy #define PLAYSOUND ...

  3. iOS 判断系统版本号的宏定义

    _IPHONE_OS_VERSION_MIN_REQUIRED 要求最低的系统版本 __IPHONE_OS_VERSION_MAX_ALLOWED 允许最高的系统版本 大多数的文档都没有正确的解释这两 ...

  4. day6 面向对象 类的定义访问 命名空间的声明 对象的定义创建 字段的定义创建访问 方法的创建调用

    day6 面向对象 面向过程是分析解决问题的步骤,然后把步骤实现 面向对象是把构成问题的事分解成各个对象,不是为了完成步骤,为了描述在整个 类是类别 是一种抽象的数据类型 是相同特征实体的抽象 类里共 ...

  5. linux驱动 打印变量,linux驱动 内核函数 变量 宏定义

    insmod modprobe(自动检测 模块加载时需要的别的模块) rmmod 用户空间工具, 加载模块到运行中的内核以及去除它们. #include module_init(init_functi ...

  6. 用宏定义代替printf函数

    问题提出 有时候我们想用宏定义来决定是编译debug版本的代码还是release的代码,dubug版本的代码会通过printf打印调试信息,release版本的代码则不会.我们总不能对每一条print ...

  7. Android CMake 编译传递宏定义参数

    在做 C++ 需求开发时经常会遇到用宏定义来区分不同版本.不同平台的功能,如下所示: #ifdef DEBUG// 调用 debug 版本方法 #elif RELEASE// 调用 release 版 ...

  8. systemverilog 宏定义 `define

    文章目录 前言 1 标准中关于`define宏的介绍 1.1 特殊符号`" 1.2 特殊符号\`\`" 1.3 特殊符号`` 2 带参数的宏`define 2.1 带参数宏的使用方 ...

  9. C语言宏定义用法总结

    前言 最近在看源代码与开发项目的时候经常会遇到一些特殊的宏用户,接接触时感觉有点奇怪,其实是自己没有全面的熟悉宏的用法.在查阅完相关的材料后,写下这一篇总结,以期待以后忘记的时候可以重新打开回忆起里面 ...

最新文章

  1. 做人工智能必看的45篇论文 | 附下载地址
  2. 洛谷——P2872 [USACO07DEC]道路建设Building Roads
  3. Java---------- LeetCode——746. 使用最小花费爬楼梯
  4. Spring Cloud 加盟重量级成员Spring Cloud Alibaba,打造更符合中国国情的微服务体系...
  5. SSH实现分页查询(转)
  6. nyoj 420(快速幂)
  7. 学习Spring Boot:(十二)Mybatis 中自定义枚举转换器
  8. python小老鼠编程_Python小老鼠编程,Python入门到精通(非常详细)
  9. Helm 3 完整教程(三):chart 的文件结构和字段详解
  10. Linux内核启动中驱动初始化过程
  11. 国密算法和GmSSL介绍
  12. matlab中小波工具箱,matlab小波分析工具箱使用教程
  13. 网络监控摄像头安装的六个问题及解决方法
  14. iphone android 传照片,教你如何在两台iPhone之间传照片
  15. 深度学习目前的局限性之AI识别彻底懵逼!这到底是「牛」还是「鲨」?
  16. i国网app苹果版_黑白直播app苹果-黑白直播苹果手机版下载
  17. 服务器安装操作系统失败,安装程序配置服务器失败怎么办
  18. 电脑本机连了VPN 在虚拟机中没有连接 如何虚拟机共享主机VPN连接
  19. Android入门第26天-在Android里自定义Adapter
  20. 【Json】——jsoncpp的序列化以及反序列化

热门文章

  1. 【培训实验记录】锐捷SDN交换机和控制器部署
  2. 网路工程师工作中常用的几款软件
  3. AS中signingConfigs配置
  4. Kanzi入门学习(一)
  5. word的链接到前一节消失
  6. Java IO完全总结(转载) --- 重点在源码分析
  7. PTA7-31藏尾诗
  8. 《塞尔达传说》与氛围游戏的兴起:在游戏中感受禅意
  9. 面试-android
  10. 大疆无人机自动避障技术盘点