it-swarm.com.ru

Создать синглтон, используя dispatch_once в GCD в Objective C

Если вы можете ориентироваться на 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;
}
337
Ryan

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

212
Dave DeLong

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;
}
35
Zelko

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
33
Sergey Petruk

Вы можете избежать выделения класса с помощью перезаписи метода 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;
}
6
i-developer

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

5
Christian

Если вы хотите убедиться, что [[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, но это нормально.

4
gnasher729

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

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

  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, вы можете определить одноэлементное свойство как 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 благоразумен.

1
Rob

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

@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

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

0
Hancock_Xu
//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
Rohit Kashyap