Обязательно прочитайте обсуждение этого вопроса / ответа тоже. Почему мы должны разделять вызовы alloc и init, чтобы избежать тупиков в Objective-C?
<Ч />
Чтобы расширить вопрос о состоянии гонки; исправление real не должно вызывать неопределенную инициализацию в вашем приложении. Неопределенная или ленивая инициализация приводит к поведению, которое может легко измениться из-за, казалось бы, безобидных изменений - конфигурации, «несвязанных» изменений кода и т. Д. ...
Лучше явно инициализировать подсистемы в заведомо удачный момент жизни программы. То есть вставьте [MyClass sharedInstance];
в метод applicationDidFinishLaunching:
вашего делегата приложения, если вам действительно нужна эта подсистема, инициализированная на ранней стадии программы (или переместите ее еще раньше, если вы хотите быть более оборонительным).
Еще лучше полностью исключить инициализацию из этого метода. То есть [MyClass initializeSharedInstance];
где +sharedInstance
утверждает (), если этот метод не вызывается первым.
Несмотря на то, что я фанат удобства, 25-летний опыт программирования ObjC научил меня, что ленивая инициализация является источником большего количества головной боли при обслуживании и рефакторинге, чем оно того стоит.
<Ч />
Хотя описанные ниже условия гонки существуют, этот код не исправляет то, что описано ниже. Это происходило в течение нескольких десятилетий, когда мы не беспокоились о параллелизме инициализаторов общих экземпляров. Оставляя неправильный код для процветания.
Имейте в виду, что для правильных ответов Колина и Харальда существует очень тонкое состояние расы, которое может привести вас в мир горя.
А именно, если -init
выделяемого класса вызывает метод sharedInstance
, он будет делать это до того, как будет установлена переменная. В обоих случаях это приведет к тупику.
Это один раз, когда вы хотите разделить alloc и init. Подбирайте код Колина, потому что это лучшее решение (при условии Mac OS X):
+(MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
// partial fix for the "new" concurrency issue
if (sharedInstance) return sharedInstance;
// partial because it means that +sharedInstance *may* return an un-initialized instance
// this is from https://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c/20895427#20895427
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
note это работает только в Mac OS X; X 10.6+ и iOS 4.0+, в частности. В старых операционных системах, где блоки недоступны, используйте блокировку или один из различных способов сделать что-то, если не на основе блоков.
<Ч />
Приведенный выше шаблон на самом деле не предотвращает проблему, описанную в тексте, и вызывает тупик при его обнаружении. Проблема в том, что dispatch_once()
не является вновь входящим и, таким образом, если init
вызывает sharedInstance
, город клин .