Перерасчет dispatch_once внутри + ?

Если я создаю одноэлемент внутри +[NSObject initialize] , мне нужно поместить мой код внутри блока dispatch_once так?

 static NSObject * Bar; @implementation Foo + (void)initialize { if (self == [Foo class]) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Bar = [NSObject new]; }); } } @end 

РЕДАКТИРОВАТЬ

Я беспокоюсь об этом, потому что хочу, чтобы все потоки увидели, что я установил Bar после +[Foo initialize] . В документации указано, что +[NSObject initialize] является потокобезопасной, но означает ли это, что она безопасна для памяти?

Ответ на ваш прямой вопрос заключается в том, что вам не нужен dispatch_once , но вам нужна проверка класса, которая у вас есть, потому что +initialize будет вызываться один раз для «не реализующего подкласса» . Он будет вызываться только один раз для конкретного класса, который вам нужен ( Foo ), поэтому dispatch_once является посторонним. Re: безопасность потоков, метод +initialize будет завершен до отправки других методов классу (или его экземплярам).

Однако вы не описываете желаемый шаблон доступа, поэтому в зависимости от того, что вы хотите, вы можете сделать обратное – если вы ожидаете, что подклассы также будут иметь доступ к Bar , тогда это будет хрупким; если подкласс инициализируется до самого Foo , тогда проверка класса предотвратит создание Bar . Если вы намереваетесь на такое поведение, используйте dispatch_once но удалите проверку класса – что обычно позволяет создавать Bar в первый раз, когда Foo или любой из его подклассов инициализируются. (Предостережение: если подкласс также переопределяет +initialize , конечно .)

Билл Бумгарнер говорит, что dispatch_once – это рекомендуемая практика Apple .

Что касается потока и безопасности памяти +initialize , благодаря этому твиту , я нашел соответствующие источники времени выполнения для проверки. objc-initialize.mm говорит:

  * Only one thread is allowed to actually initialize a class and send * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING. 

Классы могут быть инициализированы на разных потоках, и objc-initialize.mm имеет стратегию, чтобы избежать их блокировки:

 * +initialize deadlock case when a class is marked initializing while * its superclass is initialized. Solved by completely initializing * superclasses before beginning to initialize a class. * * OmniWeb class hierarchy: * OBObject * | ` OBPostLoader * OFObject * / \ * OWAddressEntry OWController * | * OWConsoleController * * Thread 1 (evil testing thread): * initialize OWAddressEntry * super init OFObject * super init OBObject * [OBObject initialize] runs OBPostLoader, which inits lots of classes... * initialize OWConsoleController * super init OWController - wait for Thread 2 to finish OWController init * * Thread 2 (normal OmniWeb thread): * initialize OWController * super init OFObject - wait for Thread 1 to finish OFObject init * * deadlock! * * Solution: fully initialize super classes before beginning to initialize * a subclass. Then the initializing+initialized part of the class hierarchy * will be a contiguous subtree starting at the root, so other threads * can't jump into the middle between two initializing classes, and we won't * get stuck while a superclass waits for its subclass which waits for the * superclass. 

Кроме того, переменные состояния инициализации класса охраняются monitor_t , который фактически определяется как :

 typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; } monitor_t; 

Поскольку это p_thread_mutex , а вызовы p_thread реализуют барьеры памяти , одинаково безопасно использовать:

 static NSObject * Bar; @implementation Foo + (void)initialize { if (self == [Foo class]) { Bar = [NSObject new]; } } @end 

а также

 static NSObject * Bar; @implementation Foo + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Bar = [NSObject new]; }); } @end 

В документах упоминается, что метод initialize вызывается только один раз для каждого класса поточно-безопасным способом. Поэтому dispatch_once не требуется.

Время выполнения отправляет инициализацию каждому классу в программе ровно один раз перед классом или любым наследуемым от него классом, отправляется его первое сообщение из программы. (Таким образом, метод никогда не может быть вызван, если класс не используется.) Время выполнения отправляет сообщение инициализации классам в потокобезопасном режиме. Суперклассы получают это сообщение перед их подклассами.

РЕДАКТИРОВАТЬ

Как отмечает @Vincent Gable в комментариях, метод initialize может вызываться более одного раза, если подкласс Foo не реализует сам метод initialize . Однако такие вызовы не будут проблемой, потому что есть проверка self == [Foo class] .

  • Основные данные и потоки / Grand Central Dispatch
  • Оптимизировать код без удаления блокировки потока
  • В GCD есть ли способ определить, является ли текущая очередь параллельной или нет?
  • NSURLConnection запущен в другом потоке. Недопустимые методы делегата
  • Какова наилучшая практика создания постоянной очереди задач в приложении iOS?
  • используя dispatch_sync в Grand Central Dispatch
  • Сбой с dispatch_block
  • как проверить, что блок dispatch_async завершен
  • Многопоточность iOS - обновления NSURLSession и UI
  • Block_release освобождение объектов пользовательского интерфейса в фоновом потоке
  • Тип 'DispatchQueue.Attributes' не имеет членства 'serial'
  • Давайте будем гением компьютера.