单例

作者: 何小八 | 来源:发表于2019-05-05 11:27 被阅读0次

单例的定义

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。

注:单例模式只应在有真正的“单一实例”的需求时才可使用。

单例的创建

单线程

+ (instancetype)shareInstance{
    static Singleton* single;
    if(!single) {      
        single = [[self alloc] init];   
    }
    return single;
}

注:上面是单线程的单例。如果有多线程同时访问,不能保证为同一个实例。

多线程

使用@synchronized加锁来创建单例

+ (instancetype)shareInstance{
    static Singleton* single;
    @synchronized(self) {
        if(!single) {           
            single = [[self alloc] init];       
        }    
    }
    return single;
}

使用@synchronized虽然解决了多线程的问题,但有性能问题。因为加锁只有在single未创建时才有必要。如果single已经创建,就不需要加锁,但程序会再次进行synchronized验证,浪费性能。
dispatch_once 能做到既解决同步多线程问题而又不影响性能。

+ (SingletonManager*)shareInstance {
    static Singleton* single;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        if(single == nil) {
            single = [[self alloc] init];
        }
    });
    return single;
}
dispatch_once的原理

dispatch_once主要是根据onceToken的值来决定怎么去执行代码。

  • 当onceToken= 0时,线程执行dispatch_once的block中代码
  • 当onceToken= -1时,线程跳过dispatch_once的block中代码不执行
  • 当onceToken为其他值时,线程被线程被阻塞,等待onceToken值改变

#ifdef __BLOCKS__
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
 
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
{
    if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
        dispatch_once(predicate, block);
    }
}
#undef dispatch_once
#define dispatch_once _dispatch_once

dispatch_once_t 发现其是 typedef long类型

静态变量在程序运行期间只被初始化一次,然后其在下一次被访问时,其值都是上次的值,其在除了这个初始化方法以外的任何地方都不能直接修改这两个变量的值。这是单例只被初始化一次的前提。

先声明了dispatch_once 函数,下面又实现了_dispatch_once 函数。
先取消dispatch_once 的定义, 然后把_dispatch_once 定义为dispatch_once
所以用户调用 dispatch_once 函数,实际上调用的是_dispatch_once 函数;
而真正的dispatch_once 函数是在_dispatch_once 内调用的。
有一个判断条件是一个宏DISPATCH_EXPECT,而判断条件为 DISPATCH_EXPECT(*predicate, ~0l) ,就是说,*predicate 很可能是 ~0l ,而当 DISPATCH_EXPECT(*predicate, ~0l) 不是 ~0! 时才调用真正的dispatch_once 函数。
~0l的意思是长整型0按位取反,其实就是长整型的-1

DISPATCH_EXPECT的宏定义为

#if __GNUC__
#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))
#else
#define DISPATCH_EXPECT(x, v) (x)

意思是如果没有定义__GNUC__的话 DISPATCH_EXPECT(x, v)就是第一个参数(x)
__GNUC__只代表gcc的主版本号
__builtin_expect 作用是允许程序员将最有可能执行的分支告诉编译器。
这个指令的写法为:__builtin_expect(EXP, N), 意思是:EXP==N的概率很大。
目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

总结

第一次运行,predicate的值是默认值0,线程执行dispatch_once,执行完之后predicate=-1,当另一个线程再次访问,predicate=-1,不执行dispatch_once
如果有两个进程同时运行到dispatch_once 方法时,这个两个进程获取到的predicate 值都是0,那么最终两个进程都会调用 最原始那个dispatch_once 函数。
猜测:在极端情况下,也有可能出现2个实例。dispatch_once也不是线程安全。

相关文章

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • 2020-11-02-Spring单例 vs. 单例模式

    Spring 单例不是 Java 单例。本文讨论 Spring 的单例与单例模式的区别。 前言 单例是 Sprin...

  • IOS学习笔记之单例

    单例介绍 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一种常用的软件设计模...

  • OC - 单例模式

    导读: 一、什么是单例模式 二、单例的作用 三、常见的单例类 四、自定义单例类的方法 一、什么是单例模式 单例模式...

  • 单例

    单例 单例宏

  • 单例模式

    特点 单例类只有1个实例对象 该单例对象必须由单例类自行创建 单例类对外提供一个访问该单例的全局访问点 结构 单例...

  • 关于java单例模式,这篇已经讲得很清楚了,建议收藏!

    概念 java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。 特点 单例...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

网友评论

      本文标题:单例

      本文链接:https://www.haomeiwen.com/subject/ifrcoqtx.html