文档章节

iOS 单例模式全面解析

coderHan
 coderHan
发布于 2015/12/13 14:44
字数 943
阅读 29
收藏 0


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

  1. static AccountManager *DefaultManager = nil;  

  2.    

  3. + (AccountManager *)defaultManager {  

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

  5.     return DefaultManager;  

  6. }  

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>



© 著作权归作者所有

coderHan
粉丝 0
博文 9
码字总数 1867
作品 0
朝阳
私信 提问
如何判断你是合格的高级iOS开发工程师?

前言 随着移动互联网的高速发展泄洪而来,有意学习移动开发的人越来越多了,竞争也是越来越大,需要学习的东西很多。如何才能在激烈的移动开发者竞争中一枝独秀,成为一名真正合格的高级iOS...

_小迷糊
2018/05/26
0
0
移动开发之设计模式- 代理模式(IOS&Android)

资源 完全参照 代理模式|菜鸟教程但不包括IOS代码 代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有...

FlanneryZJ
2018/12/18
0
0
移动开发之设计模式-工厂模式(IOS&Android)

资源 完全参照 工厂模式|菜鸟教程 ,但不包括IOS代码 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳...

FlanneryZJ
2018/12/17
0
0
移动开发之设计模式- 命令模式(IOS&Android)

资源 完全参照 命令模式|菜鸟教程但不包括IOS代码 命令模式 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用...

FlanneryZJ
2018/12/19
0
0
移动开发之设计模式-抽象工厂模式(IOS&Android)

资源 完全参照 抽象工厂模式|菜鸟教程 ,但不包括IOS代码 抽象工厂模式 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型...

FlanneryZJ
2018/12/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

代理模式之JDK动态代理 — “JDK Dynamic Proxy“

动态代理的原理是什么? 所谓的动态代理,他是一个代理机制,代理机制可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以有效的让调...

code-ortaerc
今天
4
0
学习记录(day05-标签操作、属性绑定、语句控制、数据绑定、事件绑定、案例用户登录)

[TOC] 1.1.1标签操作v-text&v-html v-text:会把data中绑定的数据值原样输出。 v-html:会把data中值输出,且会自动解析html代码 <!--可以将指定的内容显示到标签体中--><标签 v-text=""></......

庭前云落
今天
7
0
VMware vSphere的两种RDM磁盘

在VMware vSphere vCenter中创建虚拟机时,可以添加一种叫RDM的磁盘。 RDM - Raw Device Mapping,原始设备映射,那么,RDM磁盘是不是就可以称作为“原始设备映射磁盘”呢?这也是一种可以热...

大别阿郎
今天
10
0
【AngularJS学习笔记】02 小杂烩及学习总结

本文转载于:专业的前端网站☞【AngularJS学习笔记】02 小杂烩及学习总结 表格示例 <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names | orderBy ......

前端老手
昨天
14
0
Linux 内核的五大创新

在科技行业,创新这个词几乎和革命一样到处泛滥,所以很难将那些夸张的东西与真正令人振奋的东西区分开来。Linux内核被称为创新,但它又被称为现代计算中最大的奇迹,一个微观世界中的庞然大...

阮鹏
昨天
18
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部