如何实现与ARC兼容的Objective-C单例?

时间:2011-09-27 12:14:39

标签: objective-c ios singleton automatic-ref-counting

在Xcode 4.2中使用自动引用计数(ARC)时,如何转换(或创建)编译和行为正确的单例类?

10 个答案:

答案 0 :(得分:373)

与你(应该)已经完全相同的方式:

+ (instancetype)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

答案 1 :(得分:8)

如果你想根据需要创建其他实例。这个:

+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

否则,你应该这样做:

+ (id)allocWithZone:(NSZone *)zone
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}

答案 2 :(得分:5)

This is a version for ARC and non-ARC

如何使用:

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end

答案 3 :(得分:2)

这是我在ARC下的模式。 使用GCD满足新的模式,也满足Apple旧的实例化预防模式。

@implementation AAA
+ (id)alloc
{
    return  [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
    [self doesNotRecognizeSelector:_cmd];
    abort();
}
+ (instancetype)theController
{
    static AAA* c1  =   nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        c1  =   [[super allocWithZone:nil] init];

        // For confirm...       
        NSLog(@"%@", NSStringFromClass([c1 class]));    //  Prints AAA
        NSLog(@"%@", @([c1 class] == self));            //  Prints 1

        Class   real_superclass_obj =   class_getSuperclass(self);
        NSLog(@"%@", @(real_superclass_obj == self));   //  Prints 0
    });

    return  c1;
}
@end

答案 4 :(得分:2)

阅读此答案然后去阅读其他答案。

你必须首先了解一个单身人士的意思,如果你不理解它的要求是什么,那么你根本不会理解这个解决方案!

要成功创建Singleton,您必须能够执行以下3:

  • 如果有race condition,那么我们绝不允许同时创建多个SharedInstance实例!
  • 记住并保留多个调用之间的值。
  • 只创建一次。通过控制入口点。

dispatch_once_t只允许分配一次}来帮助您解决竞争条件

Static可帮助您“记住”其中任意数量的值 调用。怎么记得?它不允许再次创建具有您的sharedInstance的确切名称的任何新实例,它只适用于最初创建的实例。

未使用调用alloc init(即我们仍有alloc init个方法,因为我们是NSObject子类,但我们不应该在我们的sharedInstance类上使用它们,我们通过使用+(instancetype)sharedInstance来实现这一点,[UIApplication sharedApplication]仅限一次启动,无论同时来自不同线程的多次尝试并记住它的值

Cocoa自带的一些最常见的系统单身人士是:

  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]
  • {{1}}

基本上任何需要集中效果的东西都需要遵循某种单一的设计模式。

答案 5 :(得分:1)

或者,Objective-C为NSObject及其所有子类提供+(void)初始化方法。它总是在类的任何方法之前调用。

我在iOS 6中设置了一次断点,并且堆栈帧中出现了dispatch_once。

答案 6 :(得分:0)

Singleton类:在任何情况下或通过任何方式,任何人都无法创建多个类对象。

+ (instancetype)sharedInstance
{
    static ClassName *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ClassName alloc] init];
        // Perform other initialisation...
    });
    return sharedInstance;
}
//    You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only. 

-(MyClass)init
{
   return [ClassName sharedInstance];
}

答案 7 :(得分:0)

接受的答案有两个问题,可能与您的目的相关或不相关。

  1. 如果从init方法中,以某种方式再次调用sharedInstance方法(例如,因为其他对象是从那里使用单例构造的),它将导致堆栈溢出。
  2. 对于类层次结构,只有一个单例(即:层次结构中调用sharedInstance方法的第一个类),而不是层次结构中每个具体类的一个单例。
  3. 以下代码解决了这两个问题:

    + (instancetype)sharedInstance {
        static id mutex = nil;
        static NSMutableDictionary *instances = nil;
    
        //Initialize the mutex and instances dictionary in a thread safe manner
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            mutex = [NSObject new];
            instances = [NSMutableDictionary new];
        });
    
        id instance = nil;
    
        //Now synchronize on the mutex
        //Note: do not synchronize on self, since self may differ depending on which class this method is called on
        @synchronized(mutex) {
            id <NSCopying> key = (id <NSCopying>)self;
            instance = instances[key];
            if (instance == nil) {
                //Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
                id allocatedInstance = [self alloc];
    
                //Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
                //Do this right after allocation to avoid the stackoverflow problem
                if (allocatedInstance != nil) {
                    instances[key] = allocatedInstance;
                }
                instance = [allocatedInstance init];
    
                //Following code may be overly cautious
                if (instance != allocatedInstance) {
                    //Somehow the init method did not return the same instance as the alloc method
                    if (instance == nil) {
                        //If init returns nil: immediately remove the instance again
                        [instances removeObjectForKey:key];
                    } else {
                        //Else: put the instance in the dictionary instead of the allocatedInstance
                        instances[key] = instance;
                    }
                }
            }
        }
        return instance;
    }
    

答案 8 :(得分:-2)

          function uploadFile(revision) 
      {
          var file1 = _("fileToUpload").files[0];

          // FIXME: Developer Option
          alert(file1.name+" | "+file1.size+" | "+file1.type);

          var formdata = new FormData();
          formdata.append("fileToUpload", file1);
          formdata.append("revision", revision);
          var ajax = new XMLHttpRequest();
          ajax.upload.addEventListener("progress", progressHandler, false);
          ajax.addEventListener("load", completeHandler, false);
          ajax.addEventListener("error", errorHandler, false);
          ajax.addEventListener("abort", abortHandler, false);
          ajax.open("POST", "../file_upload_parser.php");
          ajax.send(formdata);    
      }

希望上面的代码可以帮助它。

答案 9 :(得分:-2)

如果你需要在swift中创建单例,

class var sharedInstance: MyClass {
    struct Singleton {
        static let instance = MyClass()
    }
    return Singleton.instance
}

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

你可以这样使用

let sharedClass = LibraryAPI.sharedInstance