Jak zaimplementować Objective-C singleton, który jest kompatybilny z ARC?

Jak przekonwertować (lub utworzyć) klasę singleton, która kompiluje i zachowuje się poprawnie przy użyciu automatycznego zliczania referencji (ARC) w Xcode 4.2?

Author: Brad Larson, 2011-09-27

10 answers

W dokładnie taki sam sposób jak ty (powinieneś) już to robić:

+ (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;
}
 366
Author: Nick Forge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-03 00:28:54

Jeśli chcesz utworzyć inną instancję jako needed.do to:

+ (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;
}
 8
Author: DongXu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-08 09:19:32

Jest to wersja dla ARC i nie-ARC

Jak używać:

MySingletonClass.h
@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end
MySingletonClass.m
#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end
 5
Author: Igor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-18 14:23:28

To mój wzór pod łukiem. Spełnia nowy wzór za pomocą GCD, a także spełnia stary wzór zapobiegania instancjacji 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
 2
Author: Eonil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-17 03:57:29

Przeczytaj tę odpowiedź, a następnie idź i przeczytaj drugą odpowiedź.

Musisz najpierw wiedzieć, co oznacza Singleton i jakie są jego wymagania, jeśli tego nie rozumiesz, to nie zrozumiesz rozwiązania-w ogóle!

Aby utworzyć Singleton z powodzeniem, musisz wykonać następujące 3:

  • jeśli istniał warunek race , to nie możemy pozwolić na tworzenie wielu instancji SharedInstance jednocześnie czas!
  • Zapamiętaj i zachowaj wartość pomiędzy wieloma wywołaniami.
  • utwórz go tylko raz. Kontrolując punkt wejścia.

dispatch_once_t pomaga rozwiązać Stan rasy , pozwalając tylko na wysłanie jego bloku tylko raz.

Static pomaga "zapamiętać" jego wartość w dowolnej liczbie inwokacje. Jak to zapamiętuje? Nie pozwala na ponowne utworzenie nowej instancji o tej dokładnej nazwie sharedInstance, po prostu działa z tą, która powstała pierwotnie.

Nie używaj wywołania alloc init (czyli mamy jeszcze alloc init metody ponieważ jesteśmy podklasą NSObject, chociaż nie powinniśmy ich używać) na naszej klasie sharedInstance, osiągamy to używając +(instancetype)sharedInstance, która jest ograniczona do zainicjowanej tylko raz, niezależnie od wielu prób z różnych wątków w tym samym czasie i zapamiętujemy jej wartość.

Niektóre z najczęstszych singletonów systemowych, które pochodzą z samego Cocoa są:

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

W zasadzie wszystko, co musiałoby mieć scentralizowany efekt, musiałoby podążać za pewnego rodzaju wzorcem projektowym Singletona.

 2
Author: Honey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:43

Alternatywnie, Objective-C dostarcza metodę + (void)initialize dla NSObject i wszystkich jego podklas. Jest zawsze wywoływana przed jakimikolwiek metodami klasy.

Ustawiłem punkt przerwania w jednym raz w iOS 6 i dispatch_once pojawił się w ramkach stosu.

 1
Author: Walt Sellers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 19:28:47

Klasa Singleton: nikt nie może utworzyć więcej niż jednego obiektu klasy w każdym przypadku lub w jakikolwiek sposób.

+ (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];
}
 0
Author: Yogi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-29 02:31:57

Istnieją dwa problemy z zaakceptowaną odpowiedzią, które mogą, ale nie muszą być istotne dla Twojego celu.

  1. Jeśli z metody INIT zostanie ponownie wywołana metoda sharedInstance (np. z powodu budowy innych obiektów, które używają singletonu) spowoduje to przepełnienie stosu.
  2. dla hierarchii klas istnieje tylko jeden singleton (mianowicie: pierwsza klasa w hierarchii, na której została wywołana metoda sharedInstance), zamiast jednego Singletona na konkretny klasy w hierarchii.

Poniższy kod rozwiązuje oba te problemy:

+ (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;
}
 0
Author: Werner Altewischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-02 15:08:54
#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

Nadzieję, że powyższy kod pomoże.

 -2
Author: kiran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-06 11:48:38

Jeśli chcesz utworzyć singleton w języku swift,

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

Lub

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

Możesz użyć tego sposobu

let sharedClass = LibraryAPI.sharedInstance
 -2
Author: muhammedkasva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-21 14:57:17