Как реализовать синглтон Objective-C, совместимый с ARC?

Как преобразовать (или создать) одноэлементный класс, который компилируется и ведет себя корректно при использовании автоматического подсчета ссылок (ARC) в Xcode 4.2?

Ответы

Ответ 1

Точно так же, как вы уже делали это:

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

если вы хотите создать другой экземпляр по мере необходимости. 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, вы должны сделать это:

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

Ответ 3

Это версия для ARC и не-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

Ответ 4

Это мой шаблон под 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

Ответ 5

Прочитайте этот ответ, а затем перейдите к другому ответу.

Сначала вы должны знать, что означает синглтон и каковы его требования, если вы этого не понимаете, чем вы не поймете решение - вообще!

Чтобы создать Singleton успешно, вы должны иметь возможность выполнить следующие 3:

  • Если было условие гонка, то мы не должны разрешать одновременное создание нескольких экземпляров вашего SharedInstance!
  • Помните и сохраняйте значение между несколькими вызовами.
  • Создайте его только один раз. Путем управления точкой входа.

dispatch_once_t помогает вам решить условие гонки, разрешив только один раз его блок.

Static помогает вам "запомнить" его значение на любом количестве вызовы. Как он помнит? Он не позволяет создавать новый экземпляр с таким точным именем вашего sharedInstance, он просто работает с тем, который был создан изначально.

Не использовать вызов alloc init (т.е. у нас все еще есть методы alloc init, так как мы являемся подклассом NSObject, хотя мы НЕ должны их использовать) в нашем классе sharedInstance, мы достигаем этого, используя +(instancetype)sharedInstance, который ограничен только один раз, независимо от нескольких попыток из разных потоков одновременно и помнить его значение.

Некоторые из наиболее распространенных системных синглтонов, которые поставляются с самим Cocoa:

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

В принципе, все, что должно было бы иметь централизованный эффект, должно было бы следовать какой-то схеме дизайна Singleton.

Ответ 6

В качестве альтернативы Objective-C предоставляет метод инициализации + (void) для NSObject и всех его подклассов. Он всегда вызывается перед любыми методами класса.

Я установил точку останова в один раз в iOS 6, а dispatch_once появился в кадрах стека.

Ответ 7

Singleton Class: Никто не может создавать более одного объекта класса в любом случае или любым способом.

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

В принятом ответе есть две проблемы, которые могут быть или не быть релевантными для вашей цели.

  • Если из метода init каким-то образом метод sharedInstance снова вызван (например, из-за того, что из него создаются другие объекты, которые используют singleton), это вызовет переполнение стека.
  • Для иерархии классов существует только один синглтон (а именно: первый класс в иерархии, на котором был вызван метод sharedInstance), вместо одного одиночного элемента для конкретного класса в иерархии.

Следующий код заботится обо всех этих проблемах:

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

Ответ 9

#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

Надежность выше кода поможет.

Ответ 10

если вам нужно создать singleton в 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