利用dispatch_once创建单例

    无论是爱还是恨,你都需要单例。实际上每个iOS或Mac OS应用都至少会有 UIApplicationNSApplication.
什么是单例呢?Wikipedia是如此定义的:
    在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
或者我的理解是:
    单例是一种类,该类只能实例化一个对象。
    尽管这是单例的实际定义,但在Foundation框架中不一定是这样。比如 NSFileMangerNSNotificationCenter,分别通过它们的类方法 defaultManagerdefaultCenter获取。尽管不是严格意义的单例,这些类方法返回一个可以在应用的所有代码中访问到的类的共享实例。在本文中我们也会采用该方法。
    使用Objective-C实现单例模式的最佳方式向来有很多争论,开发者(包括Apple在内)似乎每几年就会改变他们的想法。当Apple引入了 Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他们也引入了一个很适合用于实现单例模式的函数。
    该函数就是 dispatch_once
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
    该函数接收一个 dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为 BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块,对于本例就用于shared实例的实例化。
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如 @synchronized之类的来防止使用多个线程或者队列时不同步的问题。
    Apple的 GCD Documentation证实了这一点:
    如果被多个线程调用,该函数会同步等等直至代码块完成。
    实际要如何使用这些呢?
    好吧,假设有一个 AccountManager类,你想在整个应用中访问该类的共享实例。你可以按如下代码简单实现一个类方法:
+ (AccountManager *)sharedManager { 
    static AccountManager *sharedAccountManagerInstance = nil; 

    static dispatch_once_t predicate; dispatch_once(&predicate, ^{       
          sharedAccountManagerInstance = [[self alloc] init]; 
    });

    return sharedAccountManagerInstance; 

}
    这就意味着你任何时候访问共享实例,需要做的仅是:
AccountManager *accountManager = [AccountManager sharedManager];
    就这些,你现在在应用中就有一个共享的实例,该实例只会被创建一次。
    该方法有很多优势: 
           1 线程安全
        2 很好满足静态分析器要求
        3 和自动引用计数(ARC)兼容 
        4 仅需要少量代码
        该方法的劣势就是它仍然运行创建一个非共享的实例:
    AccountManager *accountManager = [[AccountManager alloc] init];
        有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。
     

    那么为什么dispatch_once可以做到线程安全呢

    dispatch_once可以保证代码被执行一次

    +(NSDateFormatter*)getDBDateFormat
    {
        static NSDateFormatter* format;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            format = [[LKDateFormatter alloc]init];
            format.dateFormat = @"yyyy-MM-dd HH:mm:ss";
        });
        return format;
    }
    

    dispatch_once_t的描述是typedef long dispatch_once_t;
    Description A predicate for use with the dispatch_once function.

    dispatch_once展开是

    void
    _dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
    {
        if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
            dispatch_once(predicate, block);
        }
    }
    

    ~0l 是 long 的0 取反也就是 一大堆1

    我们再展开DISPATCH_EXPECT, 是__builtin_expect((x), (v))

    __builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间乱费。并没有改变其对真值的判断。

    所以呢dispatch_once可以看成

    +(NSDateFormatter*)getDBDateFormat
    {
        static NSDateFormatter* format;
        static long onceToken = 0;
        if (onceToken != 0){
            1...
            {
                format = [[LKDateFormatter alloc]init];
                format.dateFormat = @"yyyy-MM-dd HH:mm:ss";
            }
            2...
        }
        return format;
    }
    

    我们可以猜测在下面的2...里的代码是修改了 onceToken的值
    下断点查看一下,发现在1里改变了一次
    然后在2里改成了-1
    这样我们就不难理解dispatch_once的逻辑了


    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    顾名思义,模式的特点就是保证一个类仅有一个实。因为这个模式只和一个类有关,没有类与类之间的关系,所有就不给出图示了。那么还是先说一下基本的定义。 模式(Singleton),保证一个类仅有一个实,并提供一个访问它的全局访问点。 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实。这个类可以保证没有其他实可以被创建,并且它可以提供一个访问该实的方法。平时,我们常用模式的地方通常是多线程。 因为Objective C传承了Smalltalk语言,所以在Objective C中实现模式和C++和C#以及Java都不太一样。因为要保证类型对象的一性,所以就要考虑Objective C在实化对象时候的各种方式。因为在Objective C中创建的各个类型都继承自NSObject类型,所以我们需要考虑NSObject类型里实化的方法,下面让我们展开来说。 在Objective C的实化对象的方式主要有三种,分别如下: obj = [NSObject new]; obj = [[NSObject alloc]init]; obj = [[NSObject allocWithZone]init]; NSObject类参考文档里记录第三种方法是因为历史原因遗留下来的,在当前的Objective C中已经不再使用,所以我们就不考虑这种方式了。下面让我们主要看一下前两种方式。 第一种方式,用new方法初始化其实是第二种方式的总和,当调用new方法时,其实是先调用了alloc方法进行isa(is a pointer)操作,创建指针,指向内存中的数据结构,紧接着调用了init方法对数据进行初始化,NSObject类参考文档里也有具体的说明,大家也可以查看文档,具体实现方式随后我会用代码向大家进行展示。 第二种方式看起来就很明确了,先调用alloc创建指针指向内存中的数据结构,再调用init方法初始化数据。这里需要注意的是,init方法只是起到了初始化数据的作用,其实也可以自定义初始化方法,即完全可以自定义一个普通返回NSObject类型的方法来代替init方法,即init方法是可以随意被代替的。只不过NSObject类型中new方法默认会调用init方法而已,init方法可以看作是NSObject类型的默认构造函数。 所以综上所述,其实只有alloc方法是每次必须调用的方法,那么我们只要控制住alloc方法,对此方法进行覆盖就可以保证类型对象的一性了。好了,说了这么多,让我们看看如何实现吧。

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值