вівторок, 27 червня 2017 р.

Singleton

Кто вообще бы мог подумать, что 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 сам за нас позаботится о том, чтобы создан был только один экземпляр нашего объекта. Тут стоит сделать шаг назад и описать проблему, которая являетя головной болью любого кто более-менее близко работал с потоками:

  1. Представьте что есть 2 потока.
  2. И тут каждый, одновременно создает singleton. Вроде бы и должен создаться только один объект, но потому что все происходит в один момент – бывают случаи когда создается два объекта.

"Но ведь можно сделать проверку на nil!” – скажете Вы.

А теперь, представьте более сложную ситуацию: объекта singleton не существует. Два потока хотят его создать одновременно:

  1. Поток 1 делает проверку на существование объекта. Видит что его нет, и проходит этап проверки.
  2. Поток 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] — каждый из этих методов возвращает объект-синглтон.

Немає коментарів:

Дописати коментар

HyperComments for Blogger

comments powered by HyperComments