objective-c ios singleton automatic-ref-counting

objective c - ¿Cómo implemento un singleton Objective-C que es compatible con ARC?



ios automatic-ref-counting (10)

¿Cómo convierto (o creo) una clase singleton que se compila y se comporta correctamente cuando se utiliza el recuento automático de referencias (ARC) en Xcode 4.2?


Alternativamente, Objective-C proporciona el método de inicialización + (void) para NSObject y todas sus subclases. Siempre se llama antes de cualquier método de la clase.

Establecí un punto de interrupción en una vez en iOS 6 y dispatch_once apareció en los marcos de la pila.


Este es mi patrón bajo ARC. Satisface un nuevo patrón usando GCD y también satisface el antiguo patrón de prevención de instancias de 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


Exactamente de la misma manera en que (debería) haberlo estado haciendo ya:

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


Hay dos problemas con la respuesta aceptada, que pueden o no ser relevantes para su propósito.

  1. Si desde el método init, de alguna manera se llama nuevamente al método sharedInstance (por ejemplo, porque otros objetos se construyen a partir de allí que usan el singleton) causará un desbordamiento de la pila.
  2. Para las jerarquías de clase solo hay un singleton (a saber: la primera clase en la jerarquía en la que se invocó el método sharedInstance), en lugar de un singleton por clase concreta en la jerarquía.

El siguiente código se ocupa de estos dos problemas:

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


Lee esta respuesta y luego ve y lee la otra respuesta.

Primero debes saber qué significa Singleton y cuáles son sus requisitos, si no lo entiendes, entonces no entenderás la solución, ¡en absoluto!

Para crear un Singleton con éxito, debe poder hacer lo siguiente 3:

  • Si hubo una condición de carrera , ¡no debemos permitir que se creen varias instancias de su SharedInstance al mismo tiempo!
  • Recuerde y mantenga el valor entre múltiples invocaciones.
  • Créelo solo una vez. Controlando el punto de entrada.

dispatch_once_t te ayuda a resolver una condición de carrera permitiendo que solo se envíe su bloque una vez.

Static te ayuda a "recordar" su valor en cualquier cantidad de invocaciones. ¿Cómo lo recuerda? No permite que se vuelva a crear ninguna instancia nueva con el nombre exacto de su sharedInstance, simplemente funciona con la que se creó originalmente.

No alloc invocación de alloc init (es decir, todavía tenemos métodos alloc init porque somos una subclase NSObject, aunque NO deberíamos usarlos) en nuestra clase sharedInstance, lo logramos mediante el uso de +(instancetype)sharedInstance , que está limitado para que solo se inicie una vez , independientemente de múltiples intentos de diferentes hilos al mismo tiempo y recordar su valor.

Algunos de los sistemas de Singletons más comunes que vienen con Cocoa son:

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

Básicamente, cualquier cosa que necesite tener un efecto centralizado debería seguir algún tipo de patrón de diseño de Singleton.


Singleton Class: Nadie puede crear más de un objeto de clase en ningún caso ni de ninguna manera.

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


si desea crear otra instancia según sea necesario. Haga esto:

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

de lo contrario, deberías hacer esto:

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


si necesita crear singleton en swift,

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

o

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

puedes usar de esta manera

let sharedClass = LibraryAPI.sharedInstance


Esta es una versión para ARC y sin ARC

Cómo utilizar:

MySingletonClass.h

@interface MySingletonClass : NSObject +(MySingletonClass *)sharedInstance; @end

MySingletonClass.m

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


#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

La esperanza sobre el código lo ayudará.