Ответ 1
Ну, простой способ вокруг init
состоит в том, чтобы просто не написать его, чтобы он вызывал реализацию NSObject по умолчанию (которая возвращает только self
). Затем, для вашей функции sharedInstance
, определите и вызовите закрытую функцию, которая выполняет работу, подобную init, когда вы создаете экземпляр вашего синглета. (Это позволяет избежать случайной повторной инициализации вашего синглтона.)
Однако!!! Основная проблема заключается в том, что alloc
вызывается пользователем вашего кода! Для этого я лично рекомендую маршрут Apple для переопределения allocWithZone:
...
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
Это означает, что пользователь все равно получит ваш экземпляр singleton, и они могут ошибочно использовать, как если бы они его выделили, и безопасно отпустить его один раз, поскольку этот пользовательский alloc выполняет сохранение в singleton. (Примечание: alloc
вызывает allocWithZone:
и не требует отдельного переопределения.)
Надеюсь, что это поможет! Дайте мне знать, если вы хотите получить дополнительную информацию ~
EDIT: расширенный ответ, чтобы предоставить пример и более подробную информацию -
Принимая во внимание Catfish_Man, часто не важно создавать пуленепробиваемый синглтон, а вместо этого просто пишите замечательные комментарии в своих заголовках/документации и помещайте в assert
.
Однако в моем случае мне нужен потокобезопасный lazy-load singleton, то есть он не выделяется до тех пор, пока он не будет использоваться, вместо того, чтобы автоматически выделяться при запуске приложения. Узнав, как это сделать безопасно, я подумал, что, возможно, так и пройду.
РЕДАКТИРОВАТЬ № 2: Теперь я использую GCD dispatch_once(...)
для поточно-безопасного подхода выделения одноэлементного объекта только один раз для срока службы приложения. См. Apple Docs: GCD dispatch_once. Я также добавляю allocWithZone:
бит переопределения из примера Apple old singleton и добавляет закрытый init с именем singletonInit
, чтобы предотвратить его случайное вызов несколько раз:
//Hidden/Private initialization
-(void)singletonInit
{
//your init code goes here
}
static HSCloudManager * sharedInstance = nil;
+ (HSCloudManager *) sharedManager {
static dispatch_once_t dispatchOncePredicate = 0;
dispatch_once(&dispatchOncePredicate, ^{
sharedInstance = [[super allocWithZone:NULL] init];
[sharedInstance singletonInit];//Only place you should call singletonInit
});
return sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//If coder misunderstands this is a singleton, behave properly with
// ref count +1 on alloc anyway, and still return singleton!
return [[HSCloudManager sharedManager] retain];
}
HSCloudManager
подклассы NSObject
и не переопределяет init
, оставляя только реализацию по умолчанию в NSObject
, которая, согласно документации Apple, возвращает только self. Это означает, что [[HSCloudManager alloc] init]
совпадает с [[[HSCloud Manager sharedManager] retain] self]
, что делает его безопасным как для запущенных пользователей, так и для многопоточных приложений в качестве лёгкой загрузки singleton.
Что касается вашей обеспокоенности в отношении пользовательского подкласса вашего синглтона, я бы сказал, что это просто комментарий/документ. Любое слепое подклассирование без чтения в классе требует боли!
РЕДАКТИРОВАТЬ № 3: Для совместимости с ARC просто удалите часть сохранения из переопределения allocWithZone:
, но сохраните переопределение.