Паттерн проектирования Singleton
(синглтон) используется когда в приложении необходимо иметь один единственный экземпляр класса. В Cocoa данный паттерн проектирования используется довольно часто. Например, классы NSApplication
, NSExceptionHandler
NSUserDefaults
являются синглтонами. Создать свой класс синглтон не составляет труда. Для этого необходимо перекрыть методы относящиеся к управлению памятью и реализовать метод, который создает и возвращает экземпляр класса. Создание экземпляра должно быть выполнено один раз. Рассмотрим синглтона на примере.
Создадим класс Counter
@interface Counter : NSObject {
@private
NSInteger _count;
}
@property NSInteger count;
+ (Counter *)sharedInstance;
@end
@implementation Counter
@synthesize count = _count;
+(Counter *) sharedInstance {
static Counter *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_sharedInstance == nil){
_sharedInstance = [[super allocWithZone:NULL] init];
}
});
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax;
}
- (oneway void)release {
}
- (id)autorelease {
return self;
}
@end
Первое, что мы сделали - это создали статический метод sharedInstance:
, возвращающий наш единственный экземпляр. В методе объявлена статическая переменная, хранящая экземпляр, между вызовами метода. Далее в методе проверяется, был ли создан экземпляр ранее, если экземпляр был создан ранее, то возвращаем его, иначе создаем экземпляр и создаем и возвращаем его.
Экземпляр создается с помощью функции dispatch_once()
. Функция обеспечивает потокобезопасность выполнения блока кода и гарантирует, что этот блок будет выполнен один раз за все время работы приложения.
Функция dispatch_once()
первым аргументом принимает предикат, вторым блок кода. Предикат используется для того, чтобы проверить выполнялся ли блок кода ранее.
Для того чтобы пользователи могли непосредственно создавать и инициализировать экземпляр нашего класса, мы переопределили метод allocWithZone:
- метод возвращает статический экземпляр.
Также мы переопределили методы retain:
, retainCount:
, release:
и autorelease:
для предотвращения уничтожения нашего экземпляра.
Пример использования:
Counter *counter = [Counter sharedInstance];
couner.count++;
NSLog(@"%ld", [[Counter sharedInstance] count]); // выведет 1
Counter *counter2 = [Counter alloc] init];
counter2.count++;
NSLog(@"%ld", [counter2 count]); // выведет 2