Реализация Singleton Objective-C, я делаю это правильно? - PullRequest
2 голосов
/ 18 ноября 2011

В моей классной колоде у меня есть

static Deck *gInstance = NULL;


+(Deck *) instance {
    @synchronized(self) {
        if (gInstance == NULL)
            gInstance = [[self alloc] init];
    }

    return (gInstance);
}

и метод init, который выглядит как

-(id) init {

    if (gInstance != NULL) {
        return self;
    }

    self = [super init];

    if (self) {
       // Lots of clever things
    }
    gInstance = self;
    return self;

}

Меня больше всего беспокоит, правильно ли реализован init. Пожалуйста, дайте мне знать, если то, что я написал, вам подходит.

Или ... есть ли способ сделать init приватным и не дать людям (включая меня) видеть это вообще?

Ответы [ 2 ]

6 голосов
/ 18 ноября 2011

Это своего рода нечетная одноэлементная реализация. Моя любимая реализация - использовать некоторые новые функции, присутствующие в GCD.

+ (MyObj*)sharedObject;
{
    static dispatch_once_t once;
    static MyObj *sharedObj;
    dispatch_once(&once, ^ { shared = [[MyObj alloc] init]; });
    return shared;
}

Я бы порекомендовал делать только это, ни больше, ни меньше. Принуждение к строгому синглтону только закончится болью и, как правило, бессмысленно и является анти-паттерном.

Поскольку в вашей реализации есть что-то строго "неправильное", я думаю, вы захотите вернуть gInstance в инициализаторе, когда он не равен NULL, а не self.

1 голос
/ 18 ноября 2011

В общем случае ваш конструктор не должен обеспечивать единичность. Ваш instance метод должен (что он делает). Причина этого в том, что вам может понадобиться один объект и возможность создавать другие экземпляры объекта.

Что касается предоставленного вами кода, то init не является поточно-ориентированным. Можно было бы назначить два отдельных потока на gInstance. Поток, который устанавливает его первым, будет пропускать память. И другие тонкие ошибки могут привести. Например, если синглтон был своего рода общим хранилищем данных, поток, выигравший гонку, фактически потерял бы свои данные относительно остальной части программы.

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

С точки зрения того, как реализовать это потокобезопасным способом, я видел, что один из двух говорит:

// first way
+ (Deck *)instance {
    static dispatch_once_t onceToken;
    static Deck *_shared;
    dispatch_once(&onceToken, ^{
        _shared = [[Deck alloc] init];
    });
    return _shared;
}

// second way
+ (Deck *)instance {
    static Deck *_shared;
    @synchronized(self) {
        if (_shared == nil) {
            _shared = [[Deck alloc] init];
        }
    }
    return _shared;
}

// init method
- (id)init {
    if ((self = [super init])) {
        // init stuff
    }
    return self;
}

Для использования dispatch_once требуется libdispatch, что означает минимальную ОС iOS 4.0 или OS X 10.6. Использование @synchronized(self) до этого должно работать на ОС.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...