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

时间:2022-09-02 09:49:05

How do I convert (or create) a singleton class that compiles and behaves correctly when using automatic reference counting (ARC) in Xcode 4.2?

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

10 个解决方案

#1


361  

In exactly the same way that you (should) have been doing it already:

就像你本应该做的那样:

+ (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;
}

#2


8  

if you want to create other instance as needed.do this:

如果需要创建其他实例。这样做:

+ (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;
}

else,you should do this:

其他的,你应该这样做:

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

#3


5  

This is a version for ARC and non-ARC

这是圆弧和非圆弧的一个版本

How To use:

如何使用:

MySingletonClass.h

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

MySingletonClass.m

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

#4


2  

This is my pattern under ARC. Satisfies new pattern using GCD and also satisfies Apple's old instantiation prevention pattern.

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

@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

#5


2  

Read this answer and then go and read the other answer.

读这个答案,然后去读另一个答案。

You must first know what does a Singleton mean and what are its requirements, if you don't understand it, than you won't understand the solution--at all!

您必须首先知道单例对象是什么意思以及它的需求是什么,如果您不理解它,那么您将无法理解解决方案——完全不理解!

To create a Singleton successfully you must be able to do the following 3:

要成功创建一个单例,您必须能够完成以下3个步骤:

  • If there was a race condition, then we must not allow multiple instances of your SharedInstance to be created at the same time!
  • 如果存在竞争条件,那么我们不允许同时创建共享实例的多个实例!
  • Remember and keep the value among multiple invocations.
  • 记住并保持多个调用之间的值。
  • Create it only once. By controlling the entry point.
  • 只有一次创建它。通过控制入口点。

dispatch_once_t helps you to solve a race condition by only allowing its block to be dispatched once.

dispatch_once_t仅允许分派其块一次,从而帮助您解决竞争条件。

Static helps you to “remember” its value across any number of invocations. How does it remember? It doesn't allow any new instance with that exact name of your sharedInstance to be created again it just works with the one that was created originally.

静态帮助您“记住”它在许多调用中的值。它如何记得?它不允许再创建具有sharedInstance确切名称的任何新实例,它只与最初创建的实例一起工作。

Not using calling alloc init (i.e. we still have alloc init methods since we are an NSObject subclass, though we should NOT use them) on our sharedInstance class, we achieve this by using +(instancetype)sharedInstance, which is bounded to only be initiated once, regardless of multiple attempts from different threads at the same time and remember its value.

不使用调用init(即我们还有init方法,因为我们是一个NSObject子类,尽管我们不应该使用它们)sharedInstance类,我们实现这一目标通过使用+(instancetype)sharedInstance只启动一次是有界的,不管多尝试不同的线程同时记住它的价值。

Some of the most common system Singletons that come with Cocoa itself are:

与可可相关的最常见的系统包括:

  • [UIApplication sharedApplication]
  • (UIApplication sharedApplication)
  • [NSUserDefaults standardUserDefaults]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • (NSBundle mainBundle)
  • [NSOperations mainQueue]
  • [nsoperation mainQueue]
  • [NSNotificationCenter defaultCenter]
  • [NSNotificationCenter defaultCenter]

Basically anything that would need to have centralized effect would need to follow some sort of a Singleton design pattern.

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

#6


1  

Alternatively, Objective-C provides the +(void)initialize method for NSObject and all its sub-classes. It is always called before any methods of the class.

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

I set a breakpoint in one once in iOS 6 and dispatch_once appeared in the stack frames.

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

#7


1  

Singleton Class : No one can create more than one object of class in any case or through any way.

单例类:没有人可以在任何情况下或通过任何方式创建多个类对象。

+ (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];
}

#8


0  

There are two issues with the accepted answer, which may or may not be relevant for your purpose.

对于已接受的答案有两个问题,它们可能与您的目的有关,也可能与您的目的无关。

  1. If from the init method, somehow the sharedInstance method is called again (e.g. because other objects are constructed from there which use the singleton) it will cause a stack overflow.
  2. 如果从init方法中以某种方式再次调用sharedInstance方法(例如,因为使用singleton的其他对象是在那里构造的),则会导致堆栈溢出。
  3. For class hierarchies there is only one singleton (namely: the first class in the hierarchy on which the sharedInstance method was called), instead of one singleton per concrete class in the hierarchy.
  4. 对于类层次结构,只有一个单例(即:在层次结构中调用sharedInstance方法的第一个类),而不是层次结构中每个具体类一个单例。

The following code takes care of both of these problems:

下面的代码将处理这两个问题:

+ (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 * 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;
}

#9


-2  

#import <Foundation/Foundation.h>

@interface SingleTon : NSObject

@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;

@end

#import "SingleTon.h"
@implementation SingleTon

+(SingleTon *) theSingleTon{
    static SingleTon *theSingleTon = nil;

    if (!theSingleTon) {

        theSingleTon = [[super allocWithZone:nil] init
                     ];
    }
    return theSingleTon;
}

+(id)allocWithZone:(struct _NSZone *)zone{

    return [self theSingleTon];
}

-(id)init{

    self = [super init];
    if (self) {
        // Set Variables
        _name = @"Kiran";
    }

    return self;
}

@end

Hope above code will help it out.

希望上面的代码能帮上忙。

#10


-2  

if you need to create singleton in swift,

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

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

or

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

you can use this way

你可以用这种方法

let sharedClass = LibraryAPI.sharedInstance

#1


361  

In exactly the same way that you (should) have been doing it already:

就像你本应该做的那样:

+ (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;
}

#2


8  

if you want to create other instance as needed.do this:

如果需要创建其他实例。这样做:

+ (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;
}

else,you should do this:

其他的,你应该这样做:

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

#3


5  

This is a version for ARC and non-ARC

这是圆弧和非圆弧的一个版本

How To use:

如何使用:

MySingletonClass.h

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

MySingletonClass.m

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

#4


2  

This is my pattern under ARC. Satisfies new pattern using GCD and also satisfies Apple's old instantiation prevention pattern.

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

@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

#5


2  

Read this answer and then go and read the other answer.

读这个答案,然后去读另一个答案。

You must first know what does a Singleton mean and what are its requirements, if you don't understand it, than you won't understand the solution--at all!

您必须首先知道单例对象是什么意思以及它的需求是什么,如果您不理解它,那么您将无法理解解决方案——完全不理解!

To create a Singleton successfully you must be able to do the following 3:

要成功创建一个单例,您必须能够完成以下3个步骤:

  • If there was a race condition, then we must not allow multiple instances of your SharedInstance to be created at the same time!
  • 如果存在竞争条件,那么我们不允许同时创建共享实例的多个实例!
  • Remember and keep the value among multiple invocations.
  • 记住并保持多个调用之间的值。
  • Create it only once. By controlling the entry point.
  • 只有一次创建它。通过控制入口点。

dispatch_once_t helps you to solve a race condition by only allowing its block to be dispatched once.

dispatch_once_t仅允许分派其块一次,从而帮助您解决竞争条件。

Static helps you to “remember” its value across any number of invocations. How does it remember? It doesn't allow any new instance with that exact name of your sharedInstance to be created again it just works with the one that was created originally.

静态帮助您“记住”它在许多调用中的值。它如何记得?它不允许再创建具有sharedInstance确切名称的任何新实例,它只与最初创建的实例一起工作。

Not using calling alloc init (i.e. we still have alloc init methods since we are an NSObject subclass, though we should NOT use them) on our sharedInstance class, we achieve this by using +(instancetype)sharedInstance, which is bounded to only be initiated once, regardless of multiple attempts from different threads at the same time and remember its value.

不使用调用init(即我们还有init方法,因为我们是一个NSObject子类,尽管我们不应该使用它们)sharedInstance类,我们实现这一目标通过使用+(instancetype)sharedInstance只启动一次是有界的,不管多尝试不同的线程同时记住它的价值。

Some of the most common system Singletons that come with Cocoa itself are:

与可可相关的最常见的系统包括:

  • [UIApplication sharedApplication]
  • (UIApplication sharedApplication)
  • [NSUserDefaults standardUserDefaults]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • (NSBundle mainBundle)
  • [NSOperations mainQueue]
  • [nsoperation mainQueue]
  • [NSNotificationCenter defaultCenter]
  • [NSNotificationCenter defaultCenter]

Basically anything that would need to have centralized effect would need to follow some sort of a Singleton design pattern.

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

#6


1  

Alternatively, Objective-C provides the +(void)initialize method for NSObject and all its sub-classes. It is always called before any methods of the class.

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

I set a breakpoint in one once in iOS 6 and dispatch_once appeared in the stack frames.

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

#7


1  

Singleton Class : No one can create more than one object of class in any case or through any way.

单例类:没有人可以在任何情况下或通过任何方式创建多个类对象。

+ (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];
}

#8


0  

There are two issues with the accepted answer, which may or may not be relevant for your purpose.

对于已接受的答案有两个问题,它们可能与您的目的有关,也可能与您的目的无关。

  1. If from the init method, somehow the sharedInstance method is called again (e.g. because other objects are constructed from there which use the singleton) it will cause a stack overflow.
  2. 如果从init方法中以某种方式再次调用sharedInstance方法(例如,因为使用singleton的其他对象是在那里构造的),则会导致堆栈溢出。
  3. For class hierarchies there is only one singleton (namely: the first class in the hierarchy on which the sharedInstance method was called), instead of one singleton per concrete class in the hierarchy.
  4. 对于类层次结构,只有一个单例(即:在层次结构中调用sharedInstance方法的第一个类),而不是层次结构中每个具体类一个单例。

The following code takes care of both of these problems:

下面的代码将处理这两个问题:

+ (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 * 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;
}

#9


-2  

#import <Foundation/Foundation.h>

@interface SingleTon : NSObject

@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;

@end

#import "SingleTon.h"
@implementation SingleTon

+(SingleTon *) theSingleTon{
    static SingleTon *theSingleTon = nil;

    if (!theSingleTon) {

        theSingleTon = [[super allocWithZone:nil] init
                     ];
    }
    return theSingleTon;
}

+(id)allocWithZone:(struct _NSZone *)zone{

    return [self theSingleTon];
}

-(id)init{

    self = [super init];
    if (self) {
        // Set Variables
        _name = @"Kiran";
    }

    return self;
}

@end

Hope above code will help it out.

希望上面的代码能帮上忙。

#10


-2  

if you need to create singleton in swift,

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

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

or

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

you can use this way

你可以用这种方法

let sharedClass = LibraryAPI.sharedInstance