Singleton с помощью dispatch_once



Вы можете любить его, можете ненавидеть, но иногда он очень нужен вам в вашем приложении. Фактически, любое приложение для iOS и Mac OS X имеют как минимум один — UIApplication или NSApplication.

Итак, что же такое Singleton. Википедия формулирует так:


 Гарантирует, что у класса есть только один экземпляр, и предоставляет 
к нему глобальную точку доступа.


Или короче


Singleton - это класс, который имеет единственный экземпляр




Несмотря на определение паттерна, оно не всегда имеет место в мире библиотеки Foundation. Например, NSFileManager или NSNotificationCenter дают доступ к своим методом через методы класса defaultManager и defaultCenter. Однако, фактически они не являются Singleton'ами в полной мере. Такого же подхода будем придерживаться по ходу статьи.

Существует много споров по поводу того, как правильно реализовывать паттерн на языке Objective-C, и даже разработчики Apple пару раз меняли свою точку зрения. Тогда, когда Apple представила технологию Grand Central Dispatch (GCD) (в Mac OS X 10.6 и iOS 4.0), была введена функция, которая прекрасно подходит для реализации паттерна Singleton:

Эта функция — dispatch_once:


void dispatch_once( dispatch_once_t* predicate, dispatch_block_t block );


Эта функция принимает предикат (который фактически имеет тип long, но действует как BOOL), затем функция dispatch_once использует его для того, чтобы проверить, что блок уже выполнялся. Кроме того, вторым параметром она принимает блок кода, который должен выполнится только один раз в течении работы приложения. Для нас — это нужный нам экземпляр.

Кроме того, что функция dispatch_once гарантирует нам то, что блок кода будет выполнен только один раз, она еще гарантирует нам потокобезопасность. Поэтому больше не надо колдовать над @synchronized.

Это подтверждается документацией Apple по GCD:


 If called simultaneously from multiple threads, this function waits
synchronously until the block has completed.



 Если функция вызывается одновременно из нескольких потоков, то функция
ожидает синхронно, пока блок будет выполнен.


Итак, как же использовать подход на практике

Представим, что у вас есть класс AccountManager, и вы хотите сделать его Singleton'ом. Вы можете реализовать это следующим образом:


+( AccountManager* ) sharedManager
{
  static AccountManager* sharedAccountManagerInstance = nil;
  static dispatch_once_t predicate;
  dispatch_once( &predicate, ^{
    sharedAccountManagerInstance = [ [ self alloc ] init ];
  } );
  return sharedAccountManagerInstance;
}


И после этого, вы сможете получать доступ к экземпляру следующим способом:


AccountManager* accountManager = [ AccountManager sharedManager ];


И это все. У вас есть единственный экземпляр, к которому вы можете получать доступ из любой части приложения, и создан он будет всего один раз.

Этот подход имеет несколько плюсов:

  1. Потокобезопасность
  2. Статический анализатор будет счастлив
  3. Совместим с автоматическим подсчетом ссылок (ARC)
  4. Требуется всего чуть-чуть дополнительного кода

Единственный минус подхода состоит в том, что вы можете создать еще один экземпляр самостоятельно:


AccountManager* accountManager = [ [ AccountManager alloc ] init ];


Иногда, вам даже нужно такое поведение, хотя чаще всего вам нужен именно строго один экземпляр.

Похожие записи




Комментарии (4) свернуть  |  развернуть

  • avatar
  • FiloXSee
  • 28 февраля 2012, 10:12
0
Можешь прокомментировать отличия данного метода от метода, описанного в статье itw66.ru/blog/obj_c/586.html?
0
не понял вопроса — ты ссылаешься на эту же статью
0
Ой, задумался. Долго решал в какой из статей спросить в комментарии о другой. Вот ссылка: itw66.ru/blog/obj_c/540.html
0
Разница в том, что ты используешь мьютекс и блокируешь поток — я нет. Кроме того, разница еще в том, что используется технология Grand Central Dispatch, которая появилась начиная с оси 4.0. Однако ее использование рекомендуется эпплом.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.

Warning: Unknown: write failed: No space left on device (28) in Unknown on line 0

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php5) in Unknown on line 0