Objective-C中单例模式的实现

来源:互联网 发布:巧克力 日本 知乎 编辑:程序博客网 时间:2024/05/02 01:06

单例模式在Cocoa和Cocoa Touch中非常常见。比如这两个,[UIApplication sharedApplication][NSApplication sharedApplication],大家应该都见过。但是我们应该如何在代码中实现一个单例模式呢?

如果你对苹果的文档很熟悉的话,你一定知道,在Cocoa Foundamentals Guide中有一段实现单例模式的示例代码。大致如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
/* Singleton.h */#import <Foundation/Foundation.h>@interface Singleton : NSObject+ (Singleton *)instance;@end/* Singleton.m */#import "Singleton.h"static Singleton *instance = nil;@implementation Singleton+ (Singleton *)instance {    if (!instance) {        instance = [[super allocWithZone:NULL] init];    }    return instance;}+ (id)allocWithZone:(NSZone *)zone {    return [self instance];}- (id)copyWithZone:(NSZone *)zone {    return self;}- (id)init {    if (instance) {        return instance;    }    self = [super init];    return self;}- (id)retain {    return self;}- (oneway void)release {    // Do nothing}- (id)autorelease {    return self;}- (NSUInteger)retainCount {    return NSUIntegerMax;}@end

这是一种很标准的Singleton实现,中规中矩。不过这种实现并不是线程安全的。所以各路大神都各显神威,给出了多种单例模式的实现。

Matt Gallagher在博客中放出了一个Macro,用来实现单例模式。虽然是一个宏定义的代码,但是具体实现还是很清楚的。代码如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
////  SynthesizeSingleton.h//  CocoaWithLove////  Created by Matt Gallagher on 20/10/08.//  Copyright 2009 Matt Gallagher. All rights reserved.////  Permission is given to use this source code file without charge in any//  project, commercial or otherwise, entirely at your risk, with the condition//  that any redistribution (in part or whole) of source code must retain//  this copyright and permission notice. Attribution in compiled projects is//  appreciated but not required.//#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname)  static classname *shared##classname = nil;  + (classname *)shared##classname {     @synchronized(self)     {         if (shared##classname == nil)         {             shared##classname = [[self alloc] init];         }     }          return shared##classname; }  + (id)allocWithZone:(NSZone *)zone {     @synchronized(self)     { \        if (shared##classname == nil)         {             shared##classname = [super allocWithZone:zone];             return shared##classname;         }     }          return nil; }  - (id)copyWithZone:(NSZone *)zone \{     return self; } - (id)retain{     return self; } - (NSUInteger)retainCount {     return NSUIntegerMax; }  - (void)release { }  - (id)autorelease {     return self; }

然而,eschaton则觉得这些实现都太繁琐了,他给出的实现如下:

123456789101112131415161718192021222324252627282930313233
@interface SomeManager : NSObject+ (id)sharedManager;@end/* 非线程安全的实现 */@implementation SomeManager+ (id)sharedManager {    static id sharedManager = nil;    if (sharedManager == nil) {        sharedManager = [[self alloc] init];    }    return sharedManager;}@end/* 线程安全的实现 */@implementation SomeManagerstatic id sharedManager = nil;+ (void)initialize {    if (self == [SomeManager class]) {        sharedManager = [[self alloc] init];    }}+ (id)sharedManager {    return sharedManager;}@end

关于为什么上述代码就能实现单例模式,以及关于线程安全问题的考量,请参考他的博客。

最后介绍一个比较现代的单例模式实现。为什么说现代呢?因为这种实现利用了GCD(Grand Central Dispatch)和ARC(Automatic Reference Counting)。核心代码如下:

123456789
+ (id)sharedInstance{  static dispatch_once_t pred = 0;  __strong static id _sharedObject = nil;  dispatch_once(&pred, ^{    _sharedObject = [[self alloc] init]; // or some other init method  });  return _sharedObject;}

单次初始化

GCD还提供单词初始化支持,这个与pthread中的函数  pthread_once 很相似。GCD提供的方式的优点在于它使用block而非函数指针,这就允许更自然的代码方式:

这个特性的主要用途是惰性单例初始化或者其他的线程安全数据共享。典型的单例初始化技术看起来像这样(线程安全的):

    + (id)sharedWhatever    {        static Whatever *whatever = nil;        @synchronized([Whatever class])        {            if(!whatever)                whatever = [[Whatever alloc] init];        }        return whatever;    }

这挺好的,但是代价比较昂贵;每次调用  +sharedWhatever 函数都会付出取锁的代价,即使这个锁只需要进行一次。确实有更风骚的方式来实现这个,使用类似双向锁或者是原子操作的东西,但是这样挺难弄而且容易出错。

使用GCD,我们可以这样重写上面的方法,使用函数 dispatch_once

    + (id)sharedWhatever    {        static dispatch_once_t pred;        static Whatever *whatever = nil;        dispatch_once(&pred, ^{            whatever = [[Whatever alloc] init];        });        return whatever;    }

这个稍微比 @synchronized方法简单些,并且GCD确保以更快的方式完成这些检测,它保证block中的代码在任何线程通过  dispatch_once 调用之前被执行,但它不会强制每次调用这个函数都让代码进行同步控制。实际上,如果你去看这个函数所在的头文件,你会发现目前它的实现其实是一个宏,进行了内联的初始化测试,这意味着通常情况下,你不用付出函数调用的负载代价,并且会有更少的同步控制负载。


作者还写了一个宏(gist)来方便使用,大家可以阅读作者的博文A note on Objective-C singletons了解详情。

大多数情况下,Apple官方文档里的单例模式的示例代码实现已经够用了。虽然它最繁琐,但是也是本文介绍的几种单例模式中最容易理解的一个。至于其他的实现就留给读者们根据需要选择和应用了。

static DataManager *sharedDataManager = nil;

+ (DataManager *) sharedManager
{
    @synchronized(self)
 {
        
if (sharedDataManager == nil)
  {
            [[self alloc] init];
        }
    }
 
    
return sharedDataManager;
}

+ (id)allocWithZone:(NSZone *)zone
{
    @synchronized(self)
 {
        
if(sharedDataManager == nil)
  {
            sharedDataManager 
= [super allocWithZone:zone];
            
return sharedDataManager;
        }
    }
 
    
return nil;
}

(全文完)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩不想去学校怎么办 讨厌父母的性格怎么办 老师揪孩子耳朵怎么办 老师整天骂孩子怎么办 教师被学生骂怎么办 幼儿园学生骂老师怎么办 学生骂老师外号怎么办 小孩上课很多嘴怎么办 学生老玩手机怎么办 和校长有了矛盾怎么办 家长打了我怎么办 小朋友不想去幼儿园怎么办 小朋友不想上幼儿园怎么办 高中老师打学生家长怎么办 老师偏心学生该怎么办? 老师能打孩子怎么办 有的幼儿打老师怎么办? 孩子特别怕老师怎么办 孩子跟老师认生怎么办 和搭班老师不合怎么办 学生厌学了老师怎么办 孩子很怕我怎么办 斗米报名之后怎么办 孩子鼻子长疮怎么办 六年级孩子不爱学习怎么办 孩子不要爱学习怎么办? 孩子说不愿意读书怎么办 孩子三天没吃饭怎么办 孩子三天不吃饭怎么办 初中学生不爱学习怎么办 孩子写字头歪怎么办 学习习惯差该怎么办 孩子沉迷网络游戏家长怎么办 二年级贪玩厌学怎么办 孩子太注重外表怎么办 宝宝老爱摔跟头怎么办 小孩很讨厌你怎么办 初二作业没写完怎么办 初中孩子上课困怎么办 老师不搭理孩子怎么办 心里纠结一件事怎么办