ARC ile uyumlu bir Objective-C singletonunu nasıl uygularım?


172

Xcode 4.2'de otomatik başvuru sayımı (ARC) kullanırken doğru bir şekilde derlenen ve davranan tek bir sınıfı nasıl dönüştürebilirim (veya oluşturabilirim)?


1
Yakın zamanda Matt Galloway'ın ARC ve manuel bellek yönetimi ortamları için Singletons hakkında oldukça derinlemesine bir makalesi buldum. galloway.me.uk/tutorials/singleton-classes
cescofry

Yanıtlar:


391

Tam olarak aynı şekilde yapmalısınız (zaten):

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

9
Sadece bellek yönetim hokus pokus herhangi yapmayın Elma içinde tavsiye için kullanılan developer.apple.com/library/mac/documentation/Cocoa/Conceptual/...
Christopher Pickslay

1
@MakingScienceFictionFact, sen bir göz atmak isteyebilirsiniz bu yazı
kervich

6
@ staticYöntem / işlev içinde bildirilen değişken değişkenler, yöntem / işlev staticdışında bildirilen bir değişkenle aynıdır , yalnızca bu yöntem / işlev kapsamında geçerlidir. +sharedInstanceYöntemdeki her ayrı işlem (farklı evrelerde bile) aynı sharedInstancedeğişkeni "görür" .
Nick Forge

9
Birisi [[Sınıfım tahsis] init] 'i çağırırsa ne olur? Bu yeni bir nesne yaratacaktır. Bunu nasıl önleyebiliriz (yöntemin dışında statik MyClass * sharedInstance = nil bildirmek dışında).
Ricardo Sanchez-Saez

2
Başka bir programcı dağıtılır ve sharedInstance veya benzeri bir şey çağırmaları gerektiğinde init çağırırsa, bu onların hatasıdır. Başkalarının potansiyel olarak hata yapmalarını önlemek için dilin temellerini ve temel sözleşmelerini yıkmak oldukça yanlış görünüyor. Boredzo.org/blog/archives/2009-06-17/doing-it-wrong
occulus

8

gerektiği gibi başka bir örnek oluşturmak istiyorsanız bunu yapın:

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

aksi takdirde, bunu yapmalısınız:

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

1
Doğru / Yanlış: dispatch_once()Bit, ilk örnekte bile ek örnekler almayacağınız anlamına gelir ...?
Olie,

4
@Olie: Yanlış, çünkü istemci kodu erişimi yapabilir [[MyClass alloc] init]ve sharedInstanceerişimi atlayabilir . DongXu, Peter Hosey'in Singleton makalesine bakmalısın . Daha allocWithZone:fazla örneğin oluşturulmasını initönlemek için geçersiz kılmanız gerekiyorsa, paylaşılan örneğin yeniden başlatılmasını önlemek için de geçersiz kılmanız gerekir .
jscs

Tamam, ben de öyle düşünmüştüm, dolayısıyla allocWithZone:versiyon. Teşekkürler.
Olie

2
Bu, tabutWithZone sözleşmesini tamamen ihlal eder.
okulus

1
singleton sadece "herhangi bir zamanda bellekteki tek bir nesne" anlamına gelir, bu bir şeydir, yeniden başlatılması başka bir şeydir.
DongXu

5

Bu ARC ve ARC olmayan bir sürümdür

Nasıl kullanılır:

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

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

2

ARC kapsamındaki kalıbım bu. GCD kullanarak yeni kalıbı ve Apple'ın eski örnekleme önleme kalıbını da tatmin eder.

@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

1
Bu c1, bir AAAüst sınıf örneği olmakla sonuçlanmaz mı? Sen buna gerek +allocüzerinde selfolmamasına, super.
Nick Forge

@NickForge super, süper sınıf nesne anlamına gelmez. Süper sınıf nesnesi elde edemezsiniz Sadece mesajların yöntemin süper sınıf versiyonuna yönlendirilmesi anlamına gelir. superhala selfsınıfı gösteriyor. Süper sınıf bir nesne almak istiyorsanız, çalışma zamanı yansıma işlevlerine sahip olmanız gerekir.
eonil

@NickForge And -allocWithZone:yöntemi, geçersiz kılma noktası sunmak için çalışma zamanının ayırma işlevine basit bir zincirdir. Sonuçta, selfpointer == geçerli sınıf nesnesi ayırıcıya iletilecek ve son olarak AAAörnek tahsis edilecektir.
eonil

haklısın, supersınıf yöntemlerinde nasıl çalıştığının inceliklerini unuttum .
Nick Forge

#
İmport

2

Bu yanıtı okuyun ve sonra diğer yanıtı okuyun.

Önce bir Singleton'un ne anlama geldiğini ve gerekliliklerini bilmelisiniz, eğer anlamadıysanız, çözümü anlamadığınızdan - hiç!

Başarılı bir Singleton oluşturmak için aşağıdakileri yapabilmeniz gerekir 3:

  • Bir yarış durumu olsaydı , SharedInstance sunucunuzun birden çok örneğinin aynı anda oluşturulmasına izin vermemeliyiz!
  • Birden fazla çağırma arasında değeri hatırlayın ve saklayın.
  • Yalnızca bir kez oluşturun. Giriş noktasını kontrol ederek.

dispatch_once_t çözmenize yardımcı olur engellemesinin yalnızca bir kez gönderilmesine izin vererek yarış durumunu .

Staticherhangi bir sayıda çağrının değerini “hatırlamanıza” yardımcı olur. Nasıl hatırlıyor? SharedInstance öğenizin tam adıyla yeni bir örneğin yeniden oluşturulmasına izin vermez, yalnızca orijinal olarak oluşturulan örnekle çalışır.

SharedInstance sınıfımızda çağrı kullanmamakalloc init (yani alloc initbir NSObject alt sınıfı olduğumuz için hala yöntemlerimiz var) , ancak bunu yapmak zorundayız +(instancetype)sharedInstance. bir kez başlatılan farklı parçacığı birden fazla girişimleri bakılmaksızın, ve değerini hatırlayın.

Kakao ile birlikte gelen en yaygın sistem Singletonlarından bazıları şunlardır:

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

Temel olarak merkezi bir etkiye sahip olması gereken her şeyin bir çeşit Singleton tasarım modelini izlemesi gerekir.


1

Alternatif olarak Objective-C, NSObject ve tüm alt sınıfları için + (void) başlatma yöntemini sağlar. Her zaman sınıfın herhangi bir yönteminden önce çağrılır.

İOS 6'da bir kez bir kesme noktası belirledim ve yığın çerçevelerinde dispatch_once belirdi.


0

Singleton Sınıfı: Hiç kimse hiçbir durumda veya herhangi bir yolla birden fazla sınıf nesnesi oluşturamaz.

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

1
Birisi init'i çağırırsa, init sharedInstance'ı çağırır, sharedInstance init'i çağırır, init sharedInstance'ı ikinci kez çağırır, sonra çöker! İlk olarak, bu sonsuz bir özyineleme döngüsüdür. İkinci olarak, dispatch_once çağrısının ikinci yinelemesi, dispatch_once içinden tekrar çağrılamayacağından çökecektir.
Chuck Krutsinger

0

Kabul edilen cevapta, amacınızla ilgili olan veya olmayan iki sorun vardır.

  1. İnit yönteminden bir şekilde sharedInstance yöntemi tekrar çağrılırsa (örn., Singleton kullanılan diğer nesneler oradan inşa edildiğinden) bir yığın taşmasına neden olur.
  2. Sınıf hiyerarşileri için, hiyerarşideki somut sınıf başına bir singleton yerine, yalnızca bir singleton (yani: hiyerarşideki sharedInstance yönteminin çağrıldığı birinci sınıf) vardır.

Aşağıdaki kod, bu sorunların her ikisine de bakar:

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

-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

Yukarıdaki kod umut yardımcı olacaktır.


-2

hızlı bir şekilde singleton oluşturmanız gerekiyorsa,

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

veya

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

bu şekilde kullanabilirsiniz

let sharedClass = LibraryAPI.sharedInstance
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.