EDIT
Я включил это вверху, ниже вы можете увидеть мои исторические оригинальные вопросы и реализацию. Тем не менее, я думаю, что нашел оптимальный способ предоставления метода sharedInstance без лишних блокировок, я хотел бы услышать о потенциальных опасениях по этому поводу:
// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;
// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
return (MySingleton *)sharedInstance;
}
+ (MySingleton*)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[MySingleton alloc] init];
// Replace expensive thread-safe method
// with the simpler one that just returns the allocated instance.
SEL orig = @selector(sharedInstance);
SEL new = @selector(simpleSharedInstance);
Method origMethod = class_getClassMethod(self, orig);
Method newMethod = class_getClassMethod(self, new);
method_exchangeImplementations(origMethod, newMethod);
}
}
return (MySingleton *)sharedInstance;
}
И историческая дискуссия вокруг инициализации:
Теперь я вижу, что исходный код был действительно похож на мой (ниже), за исключением того, что чек для экземпляра находился вне блокировки.
Хотя новый метод инициализации + (void) интересен, я не уверен, что мне это нравится больше. Кажется, что сейчас, чтобы получить экземпляр синглтона, вы должны всегда вызывать:
MySingleton instance = [[MySingleton alloc] init];
Разве это не правильно? Это кажется странным, и эффективнее ли, если вызов инициализации уже заблокирован для вас? Метод двойной блокировки, кажется, работает нормально для этого варианта использования, но при этом избегает блокировки (я думаю, что при потенциальной стоимости двойного выделения из-за того, что более одного потока может пройти через if).
Другая вещь, которая кажется странной, если, если метод инициализации действительно предпочтителен, почему мы не видим его в другом месте? Objective-C был в течение долгого времени, и я опасаюсь фундаментальных механизмов, которые отличаются практически от всех опубликованных примеров.
Мой код, который я сейчас использую (который отражает в других местах, включая этот ответ):
+ (MySingleton *)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [super allocWithZone:zone];
return sharedInstance; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}