23 июня 2010 г.

Singleton в Objective-c

Для создания синглтона Cocoa Fundamentals Guide предлагает:
  1. Создать статическую переменную типа синглтона и проинициализировать ее nil. 
  2. В методе-фабрике класса создать экземпляр класса и присвоить его статической переменной. 
  3. Переопределить метод allocWithZone: чтобы удостоверится что никто кроме класса - фабрики не сможет создать экземпляр класса. 
  4. Реализовать методы copyWithZone:, release, retain, retainCount и autorelease (последние четыре не нужны при использования сборщика мусора). 

Итого минимальный код для создания синглтона:

@implementation MySingleton

static MySingleton *sharedSingleton = nil;
+ (MySingleton*) sharedInstance {
    if (sharedSingleton == nil) {
        sharedSingleton = [[super allocWithZone:NULL] init];
    }
    return sharedSingleton;
}

+ (id)allocWithZone:(NSZone *)zone {
    return [[self sharedInstance] retain];
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (NSUInteger)retainCount {
    return NSUIntegerMax;
}

- (void)release {
    //не делаем ничего
}

- (id)autorelease {
    return self;
}
@end

Но чаще всего такое строгое соответствие понятию "одиночка" не требуется, а достаточно чтобы экземпляр класса можно было получить через метод класса.

@implementation MySingleton

+ (id) sharedInstance {
    static  MySingleton *sharedSingleton = nil;
    if (sharedSingleton == nil) {
        sharedSingleton = [[self alloc] init];
    }
    return sharedSingleton;
}
@end

Или можно воспользоваться особенностями рантайма objective-c и реализовать метод +initialize, который будет вызван до первого вызова методов класса.

@implementation MySingleton

static MySingleton *sharedSingleton = nil;

+ (void)initialize {
    if (self == [MySingleton class]) {
        sharedSingleton = [[self alloc] init];
    }
}

+ (id) sharedInstance {
    return sharedSingleton;
}
@end

Здесь проверка if (self == [MySingleton class]) находится потому что в случае наследования метод initialize будет вызываться как для предка, так и для потомка. 
Эти примеры не потокобезопасны, и для реальных задач необходимо обеспечить безопасный вызов метода sharedInstance в многопоточной среде (например с помощью двойной блокировки). Но нужно помнить, что в случае метода класса использовать конструкцию @synchronized (selfнельзя. поскольку значение self будет разным.

Комментариев нет:

Отправить комментарий