Правильный способ выделить общий экземпляр (синглтон)? - PullRequest
3 голосов
/ 09 марта 2010

Я смотрю на синглтоны, и мне было любопытно, как правильно делать alloc, из просмотра документов, книг и интернета, похоже, есть несколько используемых методов.

M1

static ReactorClass *sharedReactor = nil;
+(ReactorClass *)sharedInstance {
    if(sharedReactor == nil) {
        sharedReactor == [[ReactorClass alloc] init];
    }
    return sharedReactor;
}

M2

static ReactorClass *sharedReactor = nil;
+(ReactorClass *)sharedInstance {
    if(sharedReactor == nil) {
        sharedReactor == [[super allocWithZone:NULL] init];
    }
    return sharedReactor;
}

M3:

static ReactorClass *sharedReactor = nil;
+(ReactorClass *)sharedInstance {
    if(sharedReactor == nil) {
        sharedReactor == [[[self class] alloc] init];
    }
    return sharedReactor;
}

большое спасибо ...

1020 * Гэри *

Ответы [ 3 ]

3 голосов
/ 09 марта 2010

Я обычно пишу их так:

+ (id)sharedFoo
{
    static Foo *_sharedFoo;

    if (_sharedFoo == nil)
    {
        _sharedFoo = [[self alloc] init];
    }

    return _sharedFoo;
}

но учтите, что приведенный выше код не является потокобезопасным. Если вам нужна поточно-ориентированная реализация, у Криса Хансона есть хорошее предложение , которое заключается в создании экземпляра в переопределенной реализации +initialize.

2 голосов
/ 10 марта 2010

Использование [self class] в этом случае - пустая трата времени - M1 и M3 не сильно отличаются друг от друга, если не задействованы подклассы, а реальность такова, что остальная часть реализации не до этого.

Рассмотрим, что произойдет, если вы создадите подкласс ReactorClass следующим образом:

@interface MyReactorClass : ReactorClass {}
@end
@implementation MyReactorClass
@end

Если вы вызываете [MyReactorClass sharedInstance], вы можете видеть, что он читает из одной статической переменной sharedReactor. Ничего страшного, если вы создаете только один подкласс, но вам нужно это осознавать, и тот факт, что любые сторонние библиотеки, которые вы вызываете, также не используют тот же базовый класс для создаваемых ими синглетонов, о которых вы не знаете. Если, как предложил Марк, вы скопируете код M3 в свой подкласс, он будет работать лучше, но вам нужно спросить себя: «Почему я это сделал подклассом?» - вы получили небольшую выгоду и вам лучше было бы полностью ее кодировать, а не полагаться на детали реализации суперкласса.

Чтобы сделать это правильно, вы должны сохранить статический словарь (создать во время инициализации + базового класса) и вставить в него записи, основанные на фактическом классе создаваемого вами синглтона.

Беспокойство о том, стоит ли переопределять alloc или allocWithZone: опять же вещь подкласса, но на самом деле, любой, кто наследует от одноэлементного суперкласса, а затем разбирается с его методами выделения, заслуживает плохого поведения, которое они получают. Если вы хотите закодировать идеальный одноэлементный базовый класс, вы должны создать и DOCUMENT дополнительные вызываемые методы, чтобы субклассеры могли решить любую проблему, о которой они могли подумать, не вмешиваясь в вашу инфраструктуру.

Лично мои синглеты выбрасывают исключения из всех методов, начинающихся с init, и выполняют их настоящую инициализацию в неизвестном методе (хорошо, это называется _init) - что ГАРАНТИРУЕТ, что люди, неправильно использующие одноэлементный класс, не делают получить неожиданное поведение.

Это действительно сводится к тому, "насколько вы доверяете своему субклассеру". Если вы предполагаете, что он идиот, вам нужно переопределить такие вещи, как освобождение, сохранение и т. Д. Если вы предполагаете, что он следует правилам управления памятью, вы можете оставить эти вещи в покое, потому что они просто будут работать. Apple действительно немного глупа в этом отношении в своем примере кода; они несколько параноидальны, но отнюдь не дураки.

1 голос
/ 09 марта 2010

Если вы поместите M3 в реализацию ReactorClass, то это будет то же самое, что M1, выделяющий и возвращающий ReactorClass. Однако M3 может, если поместить в реализацию подкласса ReactorClass, тогда вернуть указатель на объект этого подкласса.

M2 всегда возвращает суперкласс реализации, в которой он находится.

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