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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7568935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:39:27  来源:igfitidea点击:

How do I implement an Objective-C singleton that is compatible with ARC?

objective-ciossingletonautomatic-ref-counting

提问by cescofry

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) 时,如何转换(或创建)一个能够正确编译和运行的单例类?

回答by Nick Forge

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

回答by DongXu

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

回答by Igor

This is a version for ARC and non-ARC

这是 ARC 和非 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

回答by Eonil

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

这是我在 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

回答by Honey

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.
  • 如果存在竞争条件,那么我们不能允许同时创建多个 SharedInstance 实例!
  • 记住并保持多次调用之间的值。
  • 只创建一次。通过控制入口点。

dispatch_once_thelps you to solve a race conditionby only allowing its block to be dispatched once.

dispatch_once_t通过只允许分派一次其块来帮助您解决竞争条件

Statichelps 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.

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

Not usingcalling allocinit(i.e. we still have allocinitmethods 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.

在我们的 sharedInstance 类上不使用调用allocinit(即我们仍然有allocinit方法,因为我们是一个 NSObject 子类,尽管我们不应该使用它们),我们通过 using 来实现这一点,无论来自不同线程的多次尝试+(instancetype)sharedInstance,它只能被启动一次同时记住它的价值。

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

Cocoa 自带的一些最常见的系统单例是:

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

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

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

回答by Walt Sellers

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)initialize 方法。它总是在类的任何方法之前调用。

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

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

回答by Yogi

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

回答by Werner Altewischer

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. 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.
  1. 如果从 init 方法,以某种方式再次调用 sharedInstance 方法(例如,因为从那里构造了其他使用单例的对象),它将导致堆栈溢出。
  2. 对于类层次结构,只有一个单例(即:层次结构中调用 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 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;
}

回答by kiran

#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.

希望上面的代码会有所帮助。

回答by muhammedkasva

if you need to create singleton in 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