Шаблон проектирования Одиночка (Singleton) в Objective-C (с ARC)

| Категории: Objective-C
Анна Аминева

Иллюстрация блокнота

В прошлом году я выучил насколько важными могут быть синглтоны в песочнице IOS. Чаще всего такие объекты могут без проблем создаваться и использоваться в глобальной области видимости (во всяком случае, большую часть времени). И после того как я понял, что для работы с моделями данных я могу использовать именно синглтоны, я решил изучить как Apple применяет низкоуровневые блоки кода для создания единственного экземпляра класса.

Синглтоны

Если вы создавали iPhone или iPad приложения, которые делают какие-нибудь вызовы к RESTful API, то вы уже определенно видели синглтоны, так как они довольно легко создаются (а также ими часто злоупотребляют) - для тех из вас, у кого нет опыта, простой пример:

1
2
3
4
5
6
7
8
9
+ (instanceType *)sharedInstance {
static instanceType *sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[instanceType alloc] init];
}
return sharedInstance;
}

Проблемы реализации синглтона

Но существует несколько проблем с такой реализацией, о которых вы должны знать.
Во-первых, этот метод создания класса НЕ потоково-безопасен, поэтому если несколько потоков попытаются получить доступ к этому объекту в одно и тоже время, то вы создадите условия для ситуации гонки.
Несмотря на то, что Apple показал этот пример в старой документации (iOS 4.0+), сегодня вы не должны использовать его.
Если вы захотите использовать синглтоны, используйте dispatch_once()

dispatch_once() решает эту проблему, потому что:

  1. он гарантирует, что блок будет вызван только однажды на протяжении жизни этого приложения;
  2. он потоково-безопасен, и
  3. благодаря низкоуровневой параллельности, он быстрее, чем другие методы типа @synchronous()

Потокобезопасная реализация синглтона

“При одновременном вызове из нескольких потоков, эта функция синхронно ждет до тех пор, пока блок выполнится”
Вот шаблон для создания синглтонов получше:

1
2
3
4
5
6
7
8
9
+ (instanceType *)sharedInstance {
static instanceType *sharedInstance = nil;
static dispatch_once_t onceToken; // onceToken = 0
dispatch_once(&onceToken, ^{
sharedInstance = [[instanceType alloc] init];
});
return sharedInstance;
}

Это наиболее безопасный (и наиболее эффективный) путь к созданию единственного экземпляра класса. Не существует возможного способа создания 2 экземпляров и данный пример на 100% потокобезопасен.