2019独角兽企业重金招聘Python工程师标准>>>

我先列举一个苹果官方文档中的写法。

  1. static AccountManager *DefaultManager = nil;

  2. + (AccountManager *)defaultManager {

  3. if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init];

  4. return DefaultManager;

  5. }

iOS4之后新增加的方法:

  1. + (AccountManager *)sharedManager

  2. {

  3. static AccountManager *sharedAccountManagerInstance = nil;

  4. static dispatch_once_t predicate;

  5. dispatch_once(&predicate, ^{

  6. sharedAccountManagerInstance = [[self alloc] init];

  7. });

  8. return sharedAccountManagerInstance;

  9. }

关于dispatch_once 函数官方文档解释如下:

  • Declaration

    void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);

    Parameters

    predicate

    A pointer to a dispatch_once_t structure that is used to test whether the block has completed or not.

    block

    The block object to execute once.

    Discussion

    This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block.

    If called simultaneously from multiple threads, this function waits synchronously until the block has completed.

    The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.

  • Executes a block object once and only once for the lifetime of an application.

  • 翻译之后:

  • 1.参数 predicate是一个指向dispatch_once_t结构体的指针,用来判断下面的block体是否执行完毕

  • 2.参数block即要被执行的block

  • 3.在应用中这个函数是用来初始化一个全局变量(单例)的。在用block初始化任何变量之前总是调用这个函数。如果这个函数被多个线程同时调用,那么函数会并发执行,直到block被执行完毕。换句话,函数支持多线程。predicate指针必须指向一个存储在全局或者静态存储区的变量,否则导致的结果不可预知。

  • 4.在一个应用的生命周期这个block执行且只执行一次

  • 那么按照官方文档要求我们先创建一个AccountManager类看一下,.m文件如下

  • static AccountMangager *_instance;
    @implementation AccountMangager
    + (instancetype)shareManager
    {static dispatch_once_t predicate;dispatch_once(&predicate, ^{_instance = [[self alloc] init];});return _instance;
    }

    外界引用:

  • - (void)viewDidLoad {[super viewDidLoad];AccountMangager *manager1 = [AccountMangager shareManager];NSLog(@"manager1 = %@",manager1);AccountMangager *manager2 = [AccountMangager shareManager];NSLog(@"manager2 = %@",manager2);AccountMangager *manager3 = [[AccountMangager alloc] init];NSLog(@"manager3 = %@",manager3);
    }
  • 打印:

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager1 = <AccountMangager: 0x7fc398f2cdb0>

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager2 = <AccountMangager: 0x7fc398f2cdb0>

  • 2015-12-13 14:17:54.642 单例模式[49159:17491715] manager3 = <AccountMangager: 0x7fc398f29c10>

  • 大家可以看到当我们调用shareManager方法时获取到的对象是相同的,但是当我们通过alloc和init来构造对象的时候,得到的对象却是不一样的。

  • 那么问题就来了,我们通过不同的途径得到不同的对象,显然是不行的。我们必须要确保对象的唯一性,所以我们就需要封锁用户通过alloc和init以及copy来构造对象这条道路。

  • 我们知道,创建对象的步骤分为申请内存(alloc)、初始化(init)这两个步骤,我们要确保对象的唯一性,因此在第一步这个阶段我们就要拦截它。当我们调用alloc方法时,oc内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。拷贝对象也是同样的原理,覆写copyWithZone方法,然后在这个方法中调用shareInstance方法返回单例对象

  • +(instancetype)shareManager
    {static dispatch_once_t predicate;dispatch_once(&predicate, ^{_instance = [[super allocWithZone:NULL]init ];});return _instance;
    }
    + (id)allocWithZone:(struct _NSZone *)zone
    {return [AccountMangager shareManager];
    }
    - (id)copy{return [AccountMangager shareManager];
    }

    再次运行打印:

  • 2015-12-13 14:31:57.413 单例模式[49215:17499767] manager1 = <AccountMangager: 0x7fcf58e233a0>

  • 2015-12-13 14:31:57.414 单例模式[49215:17499767] manager2 = <AccountMangager: 0x7fcf58e233a0>

  • 2015-12-13 14:31:57.414 单例模式[49215:17499767] manager3 = <AccountMangager: 0x7fcf58e233a0>

转载于:https://my.oschina.net/u/2457458/blog/543460

iOS 单例模式全面解析相关推荐

  1. iOS之深入解析dyld与ObjC关联的底层原理

    App启动与dylb加载 我们知道 dyld 的加载过程,即在 App 启动启动执行 main 函数之前,dylb 主要作了环境变量配置.共享缓存.主程序的初始化.插入动态库.链接主程序.链接动态库. ...

  2. iOS之深入解析objc_msgSend消息转发机制的底层原理

    一.抛砖引玉 objc_msgSend() 消息发送的过程就是 通过 SEL 查找 IMP 的过程 . objc_msgSend() 是用 汇编语言 实现的,使用汇编实现的优势是: 消息发送的过程需要 ...

  3. iOS之深入解析Runtime的objc_msgSend“慢速查找”底层原理

    CacheLookup 快速查找 objc_msgSend 通过汇编 快速查找方法缓存 ,如果能找到则调用 TailCallCachedImp 直接将方法缓存起来然后进行调用,如果查找不到就跳到 Ch ...

  4. iOS之深入解析消息转发objc_msgSend的应用场景

    一.消息转发 现有如下示例: id o = [NSObject new]; [o lastObject]; 执行上面代码,程序会崩溃并抛出以下异常: [NSObject lastObject]: un ...

  5. iOS之深入解析UmbrellaFramework的封装与应用

    一.umbrella framework 将多个已经封装好的 framework 封装成一个,封装的这种 framework 就是 umbrella framework. Apple 的官方文档中明确 ...

  6. iOS之深入解析多环境配置的实现方案

    一.多 target 形式配置多环境 如下所示,选择工程 TARGETS,新创建一个 targets: 创建完成后,可要发现产生了一个 plist 文件,这个 plist 就是对应新创建的 targe ...

  7. iOS之深入解析CFRunloop的多线程隐患

    一.前言 如果还不了解 Runloop,请参考我之前的博客:iOS之深入解析Runloop的底层原理. 在苹果官方文档中,声明了 CFRunloop 是线程安全的,但是需要注意的是,Apple 使用了 ...

  8. iOS之深入解析WKWebView加载的生命周期与代理方法

    一.前言 从 WebView 开始加载一条请求,到页面完整呈现这一过程发生了什么?无论是做 WebView 性能优化还是异常问题监控与排查,都离不开对WKWebView加载的生命周期与代理方法的剖析. ...

  9. iOS之深入解析类加载的底层原理:类如何加载到内存中

    一.App 启动与 dylb 加载 App 启动会由 libdyld.dylib 库先于 main 函数调用 start,执行 _dyld_start 方法,然后运用汇编实现调用 dyldbootst ...

最新文章

  1. 经典的《JavaScript 权威指南》中的“对象”不经典
  2. git分支合并(包含学习git命令的方法)
  3. laravel框架中文手册_Laravel框架的这些你都懂的话,它核心架构基本就懂了
  4. CSS(一) 引入方式 选择器 权重
  5. html 图片高度 页面高度自适应,怎样让网页图片高度自适应宽度
  6. 用html5交换两个变量的值,Python判断两个对象相等的原理 python交换两个变量的值为什么不用中间变量...
  7. 萝卜家园win11系统32位微软原版镜像v2021.08
  8. Qt笔记-Linux程序打印带颜色的字符串
  9. Java 图片验证码的实现和模拟简单的登录
  10. N1盒子刷机经验分享
  11. MxNet创建ILSVRC2012.rec文件
  12. 一款PHP版三合一收款码_附50多款模板源码
  13. 联想电脑无法调节屏幕亮度
  14. 蚂蚁金服 RPC 框架 Sofa-Bolt 结构分析
  15. android 玻璃背景,Android 弹窗毛玻璃背景实践
  16. 创建第一个air程序转自 IT168 静水流深
  17. 项目一15 服务器端VisualSVN安装配置 客户端TortoriesSVN安装与使用SVN
  18. 关于一次美团java程序员招聘面试的经历
  19. 科大讯飞 前端 websocket 实时语音识别 代码_科大讯飞发布智能录音笔和智能TWS耳机,搭载核心AI转写能力...
  20. 【数字化】数字化转型是什么、为什么、怎么办;2018年数字化供应链行业及案例分析报告

热门文章

  1. python多个for的执行顺序-python顺序执行多个py文件
  2. python如何爬虫网页数据-如何轻松爬取网页数据?
  3. 上海理工大学eduroam登陆
  4. DatePicker的使用(一)
  5. 网站如何获得优质链接
  6. 使用dotnet-dump 查找 .net core 3.0 占用CPU 100%的原因
  7. logging日志配置,day95下午
  8. linux redhat 下命令行全部乱码解决
  9. Github page + octopress介绍
  10. HDU 3665 Seaside(Dijkstra)