Создать синглтон, используя dispatch_once GCD в Objective-C - PullRequest
337 голосов
/ 19 апреля 2011

Если вы можете настроить таргетинг на iOS 4.0 или выше

Используя GCD, это лучший способ создать синглтон в Objective-C (поточно-ориентированный)?

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

Ответы [ 9 ]

212 голосов
/ 19 апреля 2011

Это совершенно приемлемый и потокобезопасный способ создания экземпляра вашего класса.Технически это может не быть «синглтон» (в том смысле, что может быть только один из этих объектов), но если для доступа к объекту используется только метод [Foo sharedFoo], этого достаточно.

35 голосов
/ 13 ноября 2013

instancetype

instancetype - это лишь одно из множества языковых расширений Objective-C, с каждым новым выпуском добавляется еще больше.

Знай это, люби это.

И возьмем пример того, как внимание к деталям низкого уровня может дать вам представление о новых мощных способах преобразования Objective-C.

См. Здесь: instancetype


+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

+ (Class*)sharedInstance
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}
33 голосов
/ 18 марта 2014

MySingleton.h

@interface MySingleton : NSObject

+(instancetype)sharedInstance;

+(instancetype)alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype)init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype)new __attribute__((unavailable("new not available, call sharedInstance instead")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));

@end

MySingleton.m

@implementation MySingleton

+(instancetype)sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype)initUniqueInstance {
    return [super init];
}

@end
6 голосов
/ 22 августа 2013

Вы можете избежать выделения класса с помощью перезаписи метода alloc.

@implementation MyClass

static BOOL useinside = NO;
static id _sharedObject = nil;


+(id) alloc {
    if (!useinside) {
        @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
    }
    else {
        return [super alloc];
    }
}

+(id)sharedInstance
{
    static dispatch_once_t p = 0;
    dispatch_once(&p, ^{
        useinside = YES;
        _sharedObject = [[MyClass alloc] init];
        useinside = NO;
    });   
    // returns the same object each time
    return _sharedObject;
}
5 голосов
/ 19 апреля 2011

Дейв прав, это прекрасно. Вы можете проверить документы Apple по созданию синглтона , чтобы получить советы по реализации некоторых других методов, чтобы гарантировать, что только один из них может быть создан, если классы решат НЕ использовать метод sharedFoo.

4 голосов
/ 09 апреля 2014

Если вы хотите убедиться, что [[MyClass alloc] init] возвращает тот же объект, что и sharedInstance (на мой взгляд, это необязательно, но некоторые люди этого хотят), это можно сделать очень легко и безопасно с помощью второго dispatch_once:

- (instancetype)init
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        // Your normal init code goes here. 
        sharedInstance = self;
    });

    return sharedInstance;
}

Это позволяет любой комбинации [[MyClass alloc] init] и [MyClass sharedInstance] возвращать один и тот же объект;[MyClass sharedInstance] будет чуть более эффективным.Как это работает: [MyClass sharedInstance] вызовет [[MyClass alloc] init] один раз.Другой код может вызывать его также любое количество раз.Первый вызывающий для init выполнит «нормальную» инициализацию и сохранит одноэлементный объект в методе init.Любые последующие вызовы init будут полностью игнорировать возвращаемое alloc и возвращать тот же sharedInstance;результат alloc будет освобожден.

Метод + sharedInstance будет работать как обычно.Если это не первый вызывающий, который вызвал [[MyClass alloc] init], то результат init не является результатом вызова alloc, но это нормально.

1 голос
/ 22 марта 2018

Вы спрашиваете, является ли это "лучшим способом создания синглтона".

Несколько мыслей:

  1. Во-первых, да, это потоковое решение,Этот шаблон dispatch_once - это современный, потокобезопасный способ генерации синглетонов в Objective-C.Не беспокойтесь.

  2. Вы спросили, однако, является ли это "лучшим" способом сделать это.Однако следует признать, что instancetype и [[self alloc] init] потенциально вводят в заблуждение при использовании в сочетании с синглетонами.

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

    Но static в этом методе создает проблемы для подклассов.Что если бы синглеты ImageCache и BlobCache были подклассами суперкласса Cache без реализации собственного метода sharedCache?

    ImageCache *imageCache = [ImageCache sharedCache];  // fine
    BlobCache *blobCache = [BlobCache sharedCache];     // error; this will return the aforementioned ImageCache!!!
    

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

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

  3. Для лучшей совместимости со Swift вы, вероятно, захотите определить, что этосвойство, а не метод класса, например:

    @interface Foo : NSObject
    @property (class, readonly, strong) Foo *sharedFoo;
    @end
    

    Затем вы можете написать геттер для этого свойства (реализация будет использовать предложенный вами шаблон dispatch_once):

    + (Foo *)sharedFoo { ... }
    

    Преимущество этого состоит в том, что если пользователь Swift использует его, он будет делать что-то вроде:

    let foo = Foo.shared
    

    Обратите внимание, что () нет, потому что мы реализовали его какимущество.Начиная с Swift 3, это, как правило, доступ к синглетам.Поэтому определение его как свойства помогает упростить эту совместимость.

    В качестве отступления, если вы посмотрите на то, как Apple определяет их синглтоны, это шаблон, который они приняли, например, определен их NSURLSession singleton.следующим образом:

    @property (class, readonly, strong) NSURLSession *sharedSession;
    
  4. Другим, очень незначительным соображением совместимости Swift было название синглтона.Лучше, если вы включите имя типа, а не sharedInstance.Например, если класс был Foo, вы можете определить свойство singleton как sharedFoo.Или, если класс был DatabaseManager, вы можете назвать свойство sharedManager.Тогда пользователи Swift могут сделать:

    let foo = Foo.shared
    let manager = DatabaseManager.shared
    

    Очевидно, что если вы действительно хотите использовать sharedInstance, вы всегда можете объявить имя Swift, если хотите:

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
    

    Очевидно,при написании кода Objective-C мы не должны допускать, чтобы функциональность Swift перевешивала другие соображения дизайна, но все же, если мы можем написать код, который изящно поддерживает оба языка, это предпочтительнее.

  5. Я согласенс другими, которые отмечают, что если вы хотите, чтобы это был настоящий синглтон, в котором разработчики не могут / не должны (случайно) создавать экземпляры своих собственных экземпляров, квалификатор unavailable для init и new является разумным.

0 голосов
/ 02 января 2017
//Create Singleton  
  +( instancetype )defaultDBManager
    {

        static dispatch_once_t onceToken = 0;
        __strong static id _sharedObject = nil;

        dispatch_once(&onceToken, ^{
            _sharedObject = [[self alloc] init];
        });

        return _sharedObject;
    }


//In it method
-(instancetype)init
{
    self = [super init];
  if(self)
     {
   //Do your custom initialization
     }
     return self;
}
0 голосов
/ 15 декабря 2014

Чтобы создать потокобезопасный синглтон, вы можете сделать так:

@interface SomeManager : NSObject
+ (id)sharedManager;
@end

/* thread safe */
@implementation SomeManager

static id sharedManager = nil;

+ (void)initialize {
    if (self == [SomeManager class]) {
        sharedManager = [[self alloc] init];
    }
}

+ (id)sharedManager {
    return sharedManager;
}
@end

и этот блог очень хорошо объясняет синглтон синглтоны в объекте / какао

...