代码之家  ›  专栏  ›  技术社区  ›  schwa

我的目标单打应该是什么样的?[关闭]

  •  334
  • schwa  · 技术社区  · 16 年前

    我的singleton访问方法通常是以下的一些变体:

    static MyClass *gInstance = NULL;
    
    + (MyClass *)instance
    {
        @synchronized(self)
        {
            if (gInstance == NULL)
                gInstance = [[self alloc] init];
        }
    
        return(gInstance);
    }
    

    我能做些什么来改进这个?

    26 回复  |  直到 11 年前
        1
  •  207
  •   jscs    13 年前

    另一种选择是使用 +(void)initialize 方法。从文档中:

    运行时发送 initialize 在一个程序中的每一个类,或者从它继承的任何类,从程序中发送它的第一条消息之前,只发送一次。(因此,如果不使用类,则永远不会调用该方法。)运行时将发送 初始化 以线程安全的方式向类发送消息。超类在其子类之前接收此消息。

    所以你可以做类似的事情:

    static MySingleton *sharedSingleton;
    
    + (void)initialize
    {
        static BOOL initialized = NO;
        if(!initialized)
        {
            initialized = YES;
            sharedSingleton = [[MySingleton alloc] init];
        }
    }
    
        2
  •  95
  •   Ben Hoffstein    16 年前
    @interface MySingleton : NSObject
    {
    }
    
    + (MySingleton *)sharedSingleton;
    @end
    
    @implementation MySingleton
    
    + (MySingleton *)sharedSingleton
    {
      static MySingleton *sharedSingleton;
    
      @synchronized(self)
      {
        if (!sharedSingleton)
          sharedSingleton = [[MySingleton alloc] init];
    
        return sharedSingleton;
      }
    }
    
    @end
    

    [Source]

        3
  •  59
  •   Colin Barrett    14 年前

    根据我下面的其他答案,我认为你应该这样做:

    + (id)sharedFoo
    {
        static dispatch_once_t once;
        static MyFoo *sharedFoo;
        dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
        return sharedFoo;
    }
    
        4
  •  58
  •   Community CDub    7 年前

    自从 Kendall posted 一个试图避免锁定成本的线程安全单例,我想我也会扔掉一个:

    #import <libkern/OSAtomic.h>
    
    static void * volatile sharedInstance = nil;                                                
    
    + (className *) sharedInstance {                                                                    
      while (!sharedInstance) {                                                                          
        className *temp = [[self alloc] init];                                                                 
        if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
          [temp release];                                                                                   
        }                                                                                                    
      }                                                                                                        
      return sharedInstance;                                                                        
    }
    

    好吧,让我解释一下这是如何工作的:

    1. 快速案例:正常执行 sharedInstance 已经设置,因此 while 循环永远不会执行,函数在简单测试变量的存在后返回;

    2. 慢病例:如果 共享实例 不存在,则使用比较和交换(“cas”)将实例分配并复制到其中;

    3. 争用的情况:如果两个线程都试图调用 共享实例 同时 共享实例 不同时存在,则它们将初始化该单例的新实例,并尝试将其定位到适当的位置。无论谁赢了,CAS立即返回,谁输了,释放它刚刚分配的实例并返回(现在设置) 共享实例 . 单身 OSAtomicCompareAndSwapPtrBarrier 同时充当设置线程的写入屏障和测试线程的读取屏障。

        5
  •  14
  •   Michael Nickerson    16 年前
    静态myclass*sharedinst=nil;
    
    +(ID)共享状态
    {
    @同步(自我){
    如果(sharedinst==nil){
    /*在init中设置sharedinst*/
    [[self alloc]init];
    }
    }
    返回sharedinst;
    }
    
    -(ID)初始化
    {
    如果(分享!= nIL){
    [N异常提升:N异常
    格式:@“[%@%@]无法调用;请改为使用+[%@%@”],
    nsstringfromclass([self class]),nsstringfromselector(_cmd),
    nsStringFromClass([自类]),
    nsstringfromselector(@selector(sharedInstance)“];
    }else if(self=[super init])。{
    sharedinst=自己;
    /*这里有什么具体的课程*/
    }
    返回sharedinst;
    }
    
    /*这些可能对
    一个GC应用程序。保持单身
    作为一个真正的单身汉
    非CG应用程序
    */
    -(nsuinteger)重新计数
    {
    返回nsintegermax;
    }
    
    -(单向无效)释放
    {
    }
    
    -(ID)保留
    {
    返回sharedinst;
    }
    
    -(ID)自动释放
    {
    返回sharedinst;
    }
    
        6
  •  12
  •   Community CDub    7 年前

    编辑:此实现与ARC一起废弃。请看一下 How do I implement an Objective-C singleton that is compatible with ARC? 以便正确实施。

    我在其他答案中读到的初始化的所有实现都有一个共同的错误。

    + (void) initialize {
      _instance = [[MySingletonClass alloc] init] // <----- Wrong!
    }
    
    + (void) initialize {
      if (self == [MySingletonClass class]){ // <----- Correct!
          _instance = [[MySingletonClass alloc] init] 
      }
    }
    

    Apple文档建议您检查初始化块中的类类型。因为子类在默认情况下调用初始化。存在一种不明显的情况,子类可以通过kvo间接创建。如果在另一个类中添加以下行:

    [[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
    

    Objective-C将隐式创建mysingletonclass的子类,从而导致第二次触发 +initialize .

    您可能认为应该隐式检查init块中的重复初始化,例如:

    - (id) init { <----- Wrong!
       if (_instance != nil) {
          // Some hack
       }
       else {
          // Do stuff
       }
      return self;
    }
    

    但是你会射自己的脚;或者更糟的是,给另一个开发者机会射自己的脚。

    - (id) init { <----- Correct!
       NSAssert(_instance == nil, @"Duplication initialization of singleton");
       self = [super init];
       if (self){
          // Do stuff
       }
       return self;
    }
    

    tl;dr,这是我的实现

    @implementation MySingletonClass
    static MySingletonClass * _instance;
    + (void) initialize {
       if (self == [MySingletonClass class]){
          _instance = [[MySingletonClass alloc] init];
       }
    }
    
    - (id) init {
       ZAssert (_instance == nil, @"Duplication initialization of singleton");
       self = [super init];
       if (self) {
          // Initialization
       }
       return self;
    }
    
    + (id) getInstance {
       return _instance;
    }
    @end
    

    (用我们自己的断言宏替换zassert;或者只替换nsassert。)

        7
  •  10
  •   Matthieu Cormier Adam Musch    12 年前

    关于单子宏代码的一个透彻的解释在博客cocoa with love上。

    http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .

        8
  •  9
  •   Kendall Helmstetter Gelner    15 年前

    我在sharedInstance上有一个有趣的变体,它是线程安全的,但在初始化后不锁定。我还不确定是否有足够的理由按要求修改最上面的答案,但我将其提交以供进一步讨论:

    // Volatile to make sure we are not foiled by CPU caches
    static volatile ALBackendRequestManager *sharedInstance;
    
    // There's no need to call this directly, as method swizzling in sharedInstance
    // means this will get called after the singleton is initialized.
    + (MySingleton *)simpleSharedInstance
    {
        return (MySingleton *)sharedInstance;
    }
    
    + (MySingleton*)sharedInstance
    {
        @synchronized(self)
        {
            if (sharedInstance == nil)
            {
                sharedInstance = [[MySingleton alloc] init];
                // Replace expensive thread-safe method 
                // with the simpler one that just returns the allocated instance.
                SEL origSel = @selector(sharedInstance);
                SEL newSel = @selector(simpleSharedInstance);
                Method origMethod = class_getClassMethod(self, origSel);
                Method newMethod = class_getClassMethod(self, newSel);
                method_exchangeImplementations(origMethod, newMethod);
            }
        }
        return (MySingleton *)sharedInstance;
    }
    
        9
  •  6
  •   quellish    13 年前

    简短的回答:太棒了。

    长话短说:比如……

    static SomeSingleton *instance = NULL;
    
    @implementation SomeSingleton
    
    + (id) instance {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (instance == NULL){
                instance = [[super allocWithZone:NULL] init];
            }
        });
        return instance;
    }
    
    + (id) allocWithZone:(NSZone *)paramZone {
        return [[self instance] retain];
    }
    
    - (id) copyWithZone:(NSZone *)paramZone {
        return self;
    }
    
    - (id) autorelease {
        return self;
    }
    
    - (NSUInteger) retainCount {
        return NSUIntegerMax;
    }
    
    - (id) retain {
        return self;
    }
    
    @end
    

    请务必阅读 dispatch/once.h header 了解发生了什么。在这种情况下,标题注释比文档或手册页更适用。

        10
  •  5
  •   obscenum    14 年前

    我已经将singleton回滚到一个类中,以便其他类可以继承singleton属性。

    单身:H:

    static id sharedInstance = nil;
    
    #define DEFINE_SHARED_INSTANCE + (id) sharedInstance {  return [self sharedInstance:&sharedInstance]; } \
                                   + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }
    
    @interface Singleton : NSObject {
    
    }
    
    + (id) sharedInstance;
    + (id) sharedInstance:(id*)inst;
    
    + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;
    
    @end
    

    单身汉:

    #import "Singleton.h"
    
    
    @implementation Singleton
    
    
    + (id) sharedInstance { 
        return [self sharedInstance:&sharedInstance];
    }
    
    + (id) sharedInstance:(id*)inst {
        @synchronized(self)
        {
            if (*inst == nil)
                *inst = [[self alloc] init];
        }
        return *inst;
    }
    
    + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
        @synchronized(self) {
            if (*inst == nil) {
                *inst = [super allocWithZone:zone];
                return *inst;  // assignment and return on first allocation
            }
        }
        return nil; // on subsequent allocation attempts return nil
    }
    
    - (id)copyWithZone:(NSZone *)zone {
        return self;
    }
    
    - (id)retain {
        return self;
    }
    
    - (unsigned)retainCount {
        return UINT_MAX;  // denotes an object that cannot be released
    }
    
    - (void)release {
        //do nothing
    }
    
    - (id)autorelease {
        return self;
    }
    
    
    @end
    

    下面是一些类的例子,你想成为单身汉。

    #import "Singleton.h"
    
    @interface SomeClass : Singleton {
    
    }
    
    @end
    
    @implementation SomeClass 
    
    DEFINE_SHARED_INSTANCE;
    
    @end
    

    单例类的唯一限制是它是nsObject子类。但大多数时候,我在代码中使用单例,它们实际上是nsObject子类,所以这个类真的让我的生活更轻松,代码更干净。

        11
  •  2
  •   Dana    15 年前

    这也适用于非垃圾收集环境。

    @interface MySingleton : NSObject {
    }
    
    +(MySingleton *)sharedManager;
    
    @end
    
    
    @implementation MySingleton
    
    static MySingleton *sharedMySingleton = nil;
    
    +(MySingleton*)sharedManager {
        @synchronized(self) {
            if (sharedMySingleton == nil) {
                [[self alloc] init]; // assignment not done here
            }
        }
        return sharedMySingleton;
    }
    
    
    +(id)allocWithZone:(NSZone *)zone {
        @synchronized(self) {
            if (sharedMySingleton == nil) {
                sharedMySingleton = [super allocWithZone:zone];
                return sharedMySingleton;  // assignment and return on first allocation
            }
        }
        return nil; //on subsequent allocation attempts return nil
    }
    
    
    -(void)dealloc {
        [super dealloc];
    }
    
    -(id)copyWithZone:(NSZone *)zone {
        return self;
    }
    
    
    -(id)retain {
        return self;
    }
    
    
    -(unsigned)retainCount {
        return UINT_MAX;  //denotes an object that cannot be release
    }
    
    
    -(void)release {
        //do nothing    
    }
    
    
    -(id)autorelease {
        return self;    
    }
    
    
    -(id)init {
        self = [super init];
        sharedMySingleton = self;
    
        //initialize here
    
        return self;
    }
    
    @end
    
        12
  •  2
  •   Jompe    14 年前

    这是不是应该是threadsafe,避免在第一次呼叫后进行昂贵的锁定?

    + (MySingleton*)sharedInstance
    {
        if (sharedInstance == nil) {
            @synchronized(self) {
                if (sharedInstance == nil) {
                    sharedInstance = [[MySingleton alloc] init];
                }
            }
        }
        return (MySingleton *)sharedInstance;
    }
    
        13
  •  2
  •   CJ Hanson    14 年前
        14
  •  2
  •   Tony    13 年前

    怎么样

    static MyClass *gInstance = NULL;
    
    + (MyClass *)instance
    {
        if (gInstance == NULL) {
            @synchronized(self)
            {
                if (gInstance == NULL)
                    gInstance = [[self alloc] init];
            }
        }
    
        return(gInstance);
    }
    

    所以您可以避免初始化后的同步开销?

        15
  •  2
  •   Fred McCann    12 年前

    要深入讨论Objective-C中的单例模式,请查看以下内容:

    Using the Singleton Pattern in Objective-C

        16
  •  1
  •   kevinlawler    11 年前

    克朗斯顿是:

    1. 子类(第n级)
    2. 电弧兼容
    3. 安全带 alloc init
    4. 懒散地装载
    5. 线程安全
    6. 无锁(使用+初始化,而不是@synchronize)
    7. 无宏指令
    8. 免烫
    9. 简单的

    KLSingleton

        17
  •  0
  •   community wiki 2 revs Rob Dotson    15 年前

    您不想自己同步…因为自我对象还不存在!最后锁定一个临时ID值。您希望确保没有其他人可以运行类方法(sharedInstance、alloc、allocWithZone等),因此需要在类对象上进行同步:

    @implementation MYSingleton
    
    static MYSingleton * sharedInstance = nil;
    
    +( id )sharedInstance {
        @synchronized( [ MYSingleton class ] ) {
            if( sharedInstance == nil )
                sharedInstance = [ [ MYSingleton alloc ] init ];
        }
    
        return sharedInstance;
    }
    
    +( id )allocWithZone:( NSZone * )zone {
        @synchronized( [ MYSingleton class ] ) {
            if( sharedInstance == nil )
                sharedInstance = [ super allocWithZone:zone ];
        }
    
        return sharedInstance;
    }
    
    -( id )init {
        @synchronized( [ MYSingleton class ] ) {
            self = [ super init ];
            if( self != nil ) {
                // Insert initialization code here
            }
    
            return self;
        }
    }
    
    @end
    
        18
  •  0
  •   Community CDub    7 年前

    只是想把这个留在这里,这样我就不会丢了。它的优势在于它可以在InterfaceBuilder中使用,这是一个巨大的优势。 This is taken from another question that I asked :

    static Server *instance;
    
    + (Server *)instance { return instance; }
    
    + (id)hiddenAlloc
    {
        return [super alloc];
    }
    
    + (id)alloc
    {
        return [[self instance] retain];
    }
    
    
    + (void)initialize
    {
        static BOOL initialized = NO;
        if(!initialized)
        {
            initialized = YES;
            instance = [[Server hiddenAlloc] init];
        }
    }
    
    - (id) init
    {
        if (instance)
            return self;
        self = [super init];
        if (self != nil) {
            // whatever
        }
        return self;
    }
    
        19
  •  0
  •   james    13 年前
    static mySingleton *obj=nil;
    
    @implementation mySingleton
    
    -(id) init {
        if(obj != nil){     
            [self release];
            return obj;
        } else if(self = [super init]) {
            obj = self;
        }   
        return obj;
    }
    
    +(mySingleton*) getSharedInstance {
        @synchronized(self){
            if(obj == nil) {
                obj = [[mySingleton alloc] init];
            }
        }
        return obj;
    }
    
    - (id)retain {
        return self;
    }
    
    - (id)copy {
        return self;
    }
    
    - (unsigned)retainCount {
        return UINT_MAX;  // denotes an object that cannot be released
    }
    
    - (void)release {
        if(obj != self){
            [super release];
        }
        //do nothing
    }
    
    - (id)autorelease {
        return self;
    }
    
    -(void) dealloc {
        [super dealloc];
    }
    @end
    
        20
  •  0
  •   Nate    13 年前

    我知道关于这个“问题”有很多评论,但是我没有看到很多人建议使用宏来定义单例。这是一种常见的模式,宏大大简化了单例。

    下面是我根据我看到的几个objc实现编写的宏。

    Singeton.h

    /**
     @abstract  Helps define the interface of a singleton.
     @param  TYPE  The type of this singleton.
     @param  NAME  The name of the singleton accessor.  Must match the name used in the implementation.
     @discussion
     Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
     */
    #define SingletonInterface(TYPE, NAME) \
    + (TYPE *)NAME;
    
    
    /**
     @abstract  Helps define the implementation of a singleton.
     @param  TYPE  The type of this singleton.
     @param  NAME  The name of the singleton accessor.  Must match the name used in the interface.
     @discussion
     Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
     */
    #define SingletonImplementation(TYPE, NAME) \
    static TYPE *__ ## NAME; \
    \
    \
    + (void)initialize \
    { \
        static BOOL initialized = NO; \
        if(!initialized) \
        { \
            initialized = YES; \
            __ ## NAME = [[TYPE alloc] init]; \
        } \
    } \
    \
    \
    + (TYPE *)NAME \
    { \
        return __ ## NAME; \
    }
    

    使用实例:

    MyManager.h

    @interface MyManager
    
    SingletonInterface(MyManager, sharedManager);
    
    // ...
    
    @end
    

    MyManager.m

    @implementation MyManager
    
    - (id)init
    {
        self = [super init];
        if (self) {
            // Initialization code here.
        }
    
        return self;
    }
    
    SingletonImplementation(MyManager, sharedManager);
    
    // ...
    
    @end
    

    为什么界面宏几乎为空?头文件和代码文件之间的代码一致性;可维护性,以防您想添加更多的自动方法或更改它。

    我正在使用初始化方法创建singleton,这是这里最流行的答案(在编写时)中使用的方法。

        21
  •  0
  •   chunkyguy    13 年前

    使用目标C类方法,我们可以避免使用通常的单例模式,从:

    [[Librarian sharedInstance] openLibrary]
    

    到:

    [Librarian openLibrary]
    

    通过将类包装在另一个类中 类方法 ,这样就不会意外地创建重复实例,因为我们不会创建任何实例!

    我写了一个更详细的博客 here :)

        22
  •  0
  •   JJD    12 年前

    要扩展@robbie hanson的示例…

    static MySingleton* sharedSingleton = nil;
    
    + (void)initialize {
        static BOOL initialized = NO;
        if (!initialized) {
            initialized = YES;
            sharedSingleton = [[self alloc] init];
        }
    }
    
    - (id)init {
        self = [super init];
        if (self) {
            // Member initialization here.
        }
        return self;
    }
    
        23
  •  0
  •   TienDC    12 年前

    我的方法很简单:

    static id instanceOfXXX = nil;
    
    + (id) sharedXXX
    {
        static volatile BOOL initialized = NO;
    
        if (!initialized)
        {
            @synchronized([XXX class])
            {
                if (!initialized)
                {
                    instanceOfXXX = [[XXX alloc] init];
                    initialized = YES;
                }
            }
        }
    
        return instanceOfXXX;
    }
    

    如果singleton已经初始化,则不会输入锁块。第二个检查如果(!初始化)是为了确保在当前线程获取锁时它还没有初始化。

        24
  •  0
  •   i_am_jorf    11 年前

    我没有通读所有的解决方案,所以如果这段代码是多余的,请原谅。

    在我看来,这是最安全的线程实现。

    +(SingletonObject *) sharedManager
    {
        static SingletonObject * sharedResourcesObj = nil;
    
        @synchronized(self)
        {
            if (!sharedResourcesObj)
            {
                sharedResourcesObj = [[SingletonObject alloc] init];
            }
        }
    
        return sharedResourcesObj;
    }
    
        25
  •  -4
  •   Gregory Higley    15 年前

    我通常使用与本·霍夫斯坦的答案大致相似的代码(我也从维基百科中得到)。我使用它是因为克里斯·汉森在他的评论中陈述的原因。

    但是,有时我需要把一个单件放到一个笔尖上,在这种情况下,我使用以下方法:

    @implementation Singleton
    
    static Singleton *singleton = nil;
    
    - (id)init {
        static BOOL initialized = NO;
        if (!initialized) {
            self = [super init];
            singleton = self;
            initialized = YES;
        }
        return self;
    }
    
    + (id)allocWithZone:(NSZone*)zone {
        @synchronized (self) {
            if (!singleton)
                singleton = [super allocWithZone:zone];     
        }
        return singleton;
    }
    
    + (Singleton*)sharedSingleton {
        if (!singleton)
            [[Singleton alloc] init];
        return singleton;
    }
    
    @end
    

    我离开执行 -retain (等等)给读者,尽管在垃圾收集环境中,上述代码是您所需要的。

        26
  •  -5
  •   Peter Mortensen icecrime    12 年前

    接受的答案,尽管它是编译的,是不正确的。

    + (MySingleton*)sharedInstance
    {
        @synchronized(self)  <-------- self does not exist at class scope
        {
            if (sharedInstance == nil)
                sharedInstance = [[MySingleton alloc] init];
        }
        return sharedInstance;
    }
    

    根据苹果文件:

    …您可以采用类似的方法,使用类对象而不是自身来同步关联类的类方法。

    即使使用“自我工作”,它也不应该,这对我来说就像是一个复制粘贴错误。 类工厂方法的正确实现是:

    + (MySingleton*)getInstance
    {
        @synchronized([MySingleton class]) 
        {
            if (sharedInstance == nil)
                sharedInstance = [[MySingleton alloc] init];
        }
        return sharedInstance;
    }