Если вы можете настроить iOS 4.0 или выше
Используя GCD, лучший способ создать singleton в Objective C (потокобезопасный)?
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Это совершенно приемлемый и потокобезопасный способ создания экземпляра вашего класса. Он не может быть технически "одиночным" (в нем может быть только один из этих объектов), но до тех пор, пока вы используете метод [Foo sharedFoo]
для доступа к объекту, это достаточно хорошо.
instancetype
является одним из множества расширений языка для Objective-C
, причем с каждым новым выпуском добавляется больше.
Знай, люби его.
И рассмотрим это как пример того, как внимание к деталям на низком уровне может дать вам представление о мощных новых способах преобразования Objective-C.
+ (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;
}
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
init
?
Дейв прав, это прекрасно. Вы можете проверить Документы Apple по созданию одноэлементного для советов по реализации некоторых других методов, чтобы гарантировать, что только один может быть создан, если классы выбирают НЕ использовать метод sharedFoo.
Вы можете избежать выделения класса с перезаписи метода 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;
}
Если вы хотите убедиться, что [[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 будет выполнять "нормальную" инициализацию и хранить объект singleton в методе init. Любые более поздние вызовы init полностью игнорируют возвращаемое значение и возвращают тот же sharedInstance; результат alloc будет освобожден.
Метод + sharedInstance будет работать так, как он всегда делал. Если первый вызывающий абонент не вызвал [[MyClass alloc] init], результат init не является результатом вызова alloc, но это нормально.
Вы спрашиваете, является ли это "лучшим способом создания singleton".
Несколько мыслей:
Во-первых, да, это поточно-безопасное решение. Этот шаблон dispatch_once
- это современный, потокобезопасный способ генерации одиночных чисел в Objective-C. Не беспокойтесь.
Вы спросили, действительно ли это "лучший" способ сделать это. Однако следует признать, что 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
выглядит так, что будет поддерживать подклассы, но это не так. Если вы намерены поддерживать подклассу, по крайней мере, включайте документацию, которая предупреждает будущих разработчиков о том, что они должны переопределить этот метод.
Для лучшей совместимости с Swift вы, вероятно, хотите определить это как свойство, а не метод класса, например:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Затем вы можете пойти и написать getter для этого свойства (реализация будет использовать шаблон dispatch_once
, который вы предложили):
+ (Foo *)sharedFoo { ... }
Преимущество этого в том, что если пользователь Swift использует его, они бы сделали что-то вроде:
let foo = Foo.shared
Обратите внимание: нет ()
, потому что мы реализовали его как свойство. Начиная с Swift 3, это то, как в общем доступе к синглонам. Поэтому определение его как свойства помогает облегчить взаимодействие.
В стороне, если вы посмотрите, как Apple определяет свои синглтоны, это шаблон, который они приняли, например. их NSURLSession
singleton определяется следующим образом:
@property (class, readonly, strong) NSURLSession *sharedSession;
Другим, очень незначительным соображением совместимости Swift было название singleton. Лучше всего, если вы можете включить имя типа, а не 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 перевешивала другие конструктивные соображения, но все же, если мы можем написать код, который изящно поддерживает оба языка, это предпочтительнее.
Я согласен с другими, которые отмечают, что если вы хотите, чтобы это был настоящий синглтон, где разработчики cant/shouldnt (случайно) создавали экземпляры своих собственных экземпляров, квалификатор unavailable
на init
и new
благоразумный.
//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;
}
Чтобы создать потокобезопасный синглтон, вы можете сделать следующее:
@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
и этот блог очень хорошо объясняет синглтон синглтоны в objc/ cocoa