Кто вообще бы мог подумать, что Singleton такой не самый просто паттерн в iOS? Вернее, что есть столько версий. Собственно, в .NET, помнится, наблюдалась точно такая же штука, но там в основном были просто апдейты к самой простой версии паттерна. Я вообще считаю, что сколько людей - столько и версий синглтона.
Итак, давайте начнем с простого – с описания.
Singleton - это такой объект, который существует в системе только в единственном экземпляре. Очень часто используется для хранения каких-то глобальных переменных, например настроек приложения.
Итак, как и все в Objective-C начнем мы естественно с создания интерфейса:
@interface ASSingletonObject : NSObject @property (nonatomic, copy) NSString *tempProperty; + (instancetype)sharedInstance; @end
Как видим, обычный объект с одним свойством и класс методом. Естественно, просто от интерфейса мы не получим всего чего ожидаем:
@implementation ASSingletonObject + (instancetype)sharedInstance { static ASSingletonObject *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [self new]; }); return sharedInstance; } @end
Собственно вот и все:) iOS сам за нас позаботится о том, чтобы создан был только один экземпляр нашего объекта. Тут стоит сделать шаг назад и описать проблему, которая являетя головной болью любого кто более-менее близко работал с потоками:
- Представьте что есть 2 потока.
- И тут каждый, одновременно создает singleton. Вроде бы и должен создаться только один объект, но потому что все происходит в один момент – бывают случаи когда создается два объекта.
"Но ведь можно сделать проверку на nil!” – скажете Вы.
А теперь, представьте более сложную ситуацию: объекта singleton не существует. Два потока хотят его создать одновременно:
- Поток 1 делает проверку на существование объекта. Видит что его нет, и проходит этап проверки.
- Поток 2 делает проверку на существование объекта, и хоть поток 1 проверку УЖЕ прошел, но объект ЕЩЕ не существует.
Для решения таких проблем, в .NET использовали locks – блокирование кода, для других потоков, пока он исполняется в каком – либо потоке. Собственно dispatch_once делает тоже самое – он просто синхронный:) Потому, ни один поток не может зайти в этот код, пока он занят.
Собственно, без GCD такое создать тоже можно, тогда наш код бы выглядел следующим образом:
+ (instancetype)sharedInstance { static ASSingletonObject *sharedInstance = nil; @synchronized (self) { if (!sharedInstance) { sharedInstance = [self new]; } } return sharedInstance; }
Получится тоже самое. Единственное, что dispatch_once() по документации быстрее:) Ну и семантически более правильнее.
Можно вообще бахнуть по хардкору, и создать макрос:
#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \ static dispatch_once_t pred = 0; \ __strong static id _sharedObject = nil; \ dispatch_once(&pred, ^{ \ _sharedObject = block(); \ }); \ return _sharedObject; \
Тогда сама реализация создания объекта будет выглядеть следующим образом:
+ (instancetype)sharedInstance { DEFINE_SHARED_INSTANCE_USING_BLOCK(^{ return [self new]; }) }
А использование простое:
[ASSingletonObject sharedInstance].tempProperty = @"Hello world"; NSLog(@"%@", [ASSingletonObject sharedInstance].tempProperty);
И соответсвенно лог:
2017-06-18 22:47:01.577 PatternsObjC[1629:88078] First Person name = Dima and surname = Surname 2017-06-27 21:56:49.460 PatternsObjC[1259:52434] Hello world
Singleton широко используется в Foundation, iOS SDK. Например: [NSUserDefaults standardUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager] — каждый из этих методов возвращает объект-синглтон.
Немає коментарів:
Дописати коментар