Ответ 1
@bbum post обновлено, чтобы упомянуть, что это решение не решает описанную проблему. Независимо от того, разделяете ли вы +alloc
и -init
или нет, эта проблема все еще существует.
Обоснование заключается в редактировании его сообщения, но для этого, dispatch_once()
не reentrant. В этом случае это означает, что вызов dispatch_once()
внутри блока dispatch_once()
(т.е. Рекурсивно) приведет к тупиковой ситуации.
Так, например, если у вас есть следующий код для +sharedInstance
:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[MyClass alloc] init]
});
return sharedInstance;
}
.. и MyClass
-init
метод прямо или косвенно также вызывает свой собственный метод класса +sharedInstance
(например, возможно, какой-то другой объект, который MyClass -init
распределяет вызовы до MyClass
+sharedInstance
), что будет означать, что вы пытаетесь вызвать dispatch_once
изнутри самого себя.
Так как dispatch_once
является потокобезопасным, синхронным и сконструированным таким образом, что он выполняет ровно один раз, вы не можете вызывать dispatch_once
еще раз, пока блок внутри не завершит выполнение один раз. Это приведет к тупиковой ситуации, поскольку второй вызов dispatch_once
будет ждать завершения первого вызова (уже в середине выполнения), а первый вызов ожидает второго (рекурсивного) вызова на dispatch_once
пройти. Они ждут друг друга, отсюда и тупик.
Если вам требуется решение, обеспечивающее повторное размещение, вам нужно будет использовать что-то вроде NSRecursiveLock
, что значительно дороже, чем dispatch_once
, который не использует механизм блокировки.
РЕДАКТИРОВАТЬ: Рассуждение для разделения +alloc
/-init
в исходном ответе @bbum в соответствии с запросом:
Исходный код @bbum, опубликованный перед его редактированием, выглядит так:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
if (sharedInstance) return sharedInstance;
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
Обратите внимание на эту строку: if (sharedInstance) return sharedInstance;
Идея здесь заключается в том, что присвоение значения non-nil sharedInstance
перед вызовом -init
приведет к возврату существующего значения sharedInstance
(возвращается из +alloc
) до нажав вызов dispatch_once()
(и избегая тупика) в случае, когда вызов -init
приводит к рекурсивному вызову +sharedInstance
, как обсуждалось ранее в моем ответе.
Однако это хрупкое исправление, потому что оператор if
не является потокобезопасным.