Реализация шаблона Singleton

Я видел эту конкретную реализацию шаблона Singleton везде:

+ (CargoBay *)sharedManager {
   static CargoBay *_sharedManager = nil;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      _sharedManager = [[CargoBay alloc] init];
   });
   return _sharedManager;
}

и это, кажется, считается хорошей практикой (в частности, это из CargoBay).

Единственная часть, которую я не понимаю, это первая строка static CargoBay *_sharedManager = nil;,

Почему вы устанавливаете это static переменная к nil?

2 ответа

Решение

Это просто вопрос читабельности, условности и практики. Это на самом деле не нужно, потому что:

Один. Его значение никогда не будет проверено. В более старых реализациях синглтона были известные

+ (id)sharedInstance
{
    static SomeClass *shared = nil;
    if (shared == nil)
        shared = [[SomeClass alloc] init];

    return shared;
}

код - чтобы этот метод работал, переменная поддержки должна быть инициализирована равной nil, так как, если бы она не была nil в первый раз, она ложно пропустила alloc-init в части if и возвратила бы указатель мусора. Однако с решением GCD ноль-проверка больше не нужна - GCD обрабатывает прагму "выполнить этот код только один раз".

Два. Но тем не менее: статические переменные неявно инициализируются нулями. Так что даже если вы просто напишите static id shared; это будет изначально nil,

Три. Почему это может быть хорошей практикой? Потому что, несмотря на первые две причины, о которых я упомянул, читателю исходного кода все же удобнее читать, что что-то явно инициализируется нулем. Или могут даже существовать некоторые несоответствующие реализации, где статические переменные не инициализируются должным образом автоматически, и тогда это действие должно быть предпринято.

Вы устанавливаете это в ноль, чтобы гарантировать, что вы получаете чистый экземпляр.

Это более читаемая версия того, что вы хотите сделать:

+ (GlobalVariables *)sharedInstance {
    // the instance of this class is stored here
    static GlobalVariables *myInstance = nil;

    // check to see if an instance already exists
    if (nil == myInstance) {
        myInstance  = [[[self class] alloc] init];
    }
    // return the instance of this class
    return myInstance;
}

Но есть множество постов, показывающих, как это потенциально не может быть потокобезопасным, поэтому переходя к гибридному методу, описанному выше, и тому, что я опубликовал, вы получите следующее:

// Declared outside Singleton Manager    
static SingletonClass *myInstance = nil;
+ (GlobalVariables *)sharedInstance {
    if (nil != myInstance) {
        return myInstance;
    }

    static dispatch_once_t pred;        // Lock
    dispatch_once(&pred, ^{             // This code is called at most once per app
        myInstance = [[GlobalVariables alloc] init];
    });

    return myInstance;
}
Другие вопросы по тегам