Кто вообще бы мог подумать, что 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] — каждый из этих методов возвращает объект-синглтон.
Немає коментарів:
Дописати коментар