Как мне реализовать синглтон Objective-C, совместимый с ARC?
Как мне преобразовать (или создать) одноэлементный класс, который компилируется и ведет себя правильно при использовании автоматического подсчета ссылок (ARC) в Xcode 4.2?
10 ответов
Точно так же, как вы (должны) уже делали это:
+ (instancetype)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
Если вы хотите создать другой экземпляр по мере необходимости. сделайте следующее:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
иначе вы должны сделать это:
+ (id)allocWithZone:(NSZone *)zone
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
Как пользоваться:
MySingletonClass.h
@interface MySingletonClass : NSObject
+(MySingletonClass *)sharedInstance;
@end
MySingletonClass.m
#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end
Прочитайте этот ответ, а затем иди и прочитайте другой ответ.
Сначала вы должны знать, что означает синглтон и каковы его требования, если вы не понимаете его, тогда вы не поймете решение - вообще!
Чтобы успешно создать синглтон, вы должны выполнить следующие 3:
- Если возникло условие гонки, мы не должны допускать одновременного создания нескольких экземпляров вашего SharedInstance!
- Помните и сохраняйте значение среди нескольких вызовов.
- Создайте его только один раз. Управляя точкой входа.
dispatch_once_t
помогает вам решить условие гонки, позволяя отправлять блок только один раз.
Static
помогает вам "запомнить" его значение при любом количестве вызовов. Как это помнить? Он не позволяет создавать новый экземпляр с таким точным именем вашего sharedInstance, он просто работает с тем, который был создан изначально.
Не использовать звонки alloc
init
(т.е. у нас еще есть alloc
init
методы, так как мы являемся подклассом NSObject, хотя мы не должны их использовать) в нашем классе sharedInstance, мы достигаем этого, используя +(instancetype)sharedInstance
, который ограничен, чтобы быть инициированным только один раз, независимо от нескольких попыток из разных потоков одновременно и запомнить его значение.
Вот некоторые из наиболее распространенных системных синглетонов, которые поставляются вместе с Какао:
[UIApplication sharedApplication]
[NSUserDefaults standardUserDefaults]
[NSFileManager defaultManager]
[NSBundle mainBundle]
[NSOperations mainQueue]
[NSNotificationCenter defaultCenter]
По сути, все, что должно иметь централизованный эффект, должно следовать какому-то шаблону проектирования Singleton.
Это мой шаблон под ARC. Удовлетворяет новый шаблон, используя GCD, а также удовлетворяет старый шаблон Apple по предотвращению создания экземпляров.
@implementation AAA
+ (id)alloc
{
return [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
[self doesNotRecognizeSelector:_cmd];
abort();
}
+ (instancetype)theController
{
static AAA* c1 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
c1 = [[super allocWithZone:nil] init];
// For confirm...
NSLog(@"%@", NSStringFromClass([c1 class])); // Prints AAA
NSLog(@"%@", @([c1 class] == self)); // Prints 1
Class real_superclass_obj = class_getSuperclass(self);
NSLog(@"%@", @(real_superclass_obj == self)); // Prints 0
});
return c1;
}
@end
В качестве альтернативы Objective-C предоставляет метод инициализации +(void) для NSObject и всех его подклассов. Он всегда вызывается перед любыми методами класса.
Я установил точку останова в один раз в iOS 6, и dispatch_once появился в кадрах стека.
Класс Singleton: Никто не может создать более одного объекта класса в любом случае или любым способом.
+ (instancetype)sharedInstance
{
static ClassName *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ClassName alloc] init];
// Perform other initialisation...
});
return sharedInstance;
}
// You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only.
-(MyClass)init
{
return [ClassName sharedInstance];
}
Есть два вопроса с принятым ответом, которые могут или не могут иметь отношение к вашей цели.
- Если из метода init каким-то образом вызывается метод sharedInstance (например, из-за того, что из него создаются другие объекты, использующие синглтон), это вызовет переполнение стека.
- Для иерархий классов существует только один синглтон (а именно: первый класс в иерархии, для которого был вызван метод sharedInstance) вместо одного синглтона на конкретный класс в иерархии.
Следующий код решает обе эти проблемы:
+ (instancetype)sharedInstance {
static id mutex = nil;
static NSMutableDictionary *instances = nil;
//Initialize the mutex and instances dictionary in a thread safe manner
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mutex = [NSObject new];
instances = [NSMutableDictionary new];
});
id instance = nil;
//Now synchronize on the mutex
//Note: do not synchronize on self, since self may differ depending on which class this method is called on
@synchronized(mutex) {
id <NSCopying> key = (id <NSCopying>)self;
instance = instances[key];
if (instance == nil) {
//Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
id allocatedInstance = [self alloc];
//Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
//Do this right after allocation to avoid the stackru problem
if (allocatedInstance != nil) {
instances[key] = allocatedInstance;
}
instance = [allocatedInstance init];
//Following code may be overly cautious
if (instance != allocatedInstance) {
//Somehow the init method did not return the same instance as the alloc method
if (instance == nil) {
//If init returns nil: immediately remove the instance again
[instances removeObjectForKey:key];
} else {
//Else: put the instance in the dictionary instead of the allocatedInstance
instances[key] = instance;
}
}
}
}
return instance;
}
#import <Foundation/Foundation.h>
@interface SingleTon : NSObject
@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;
@end
#import "SingleTon.h"
@implementation SingleTon
+(SingleTon *) theSingleTon{
static SingleTon *theSingleTon = nil;
if (!theSingleTon) {
theSingleTon = [[super allocWithZone:nil] init
];
}
return theSingleTon;
}
+(id)allocWithZone:(struct _NSZone *)zone{
return [self theSingleTon];
}
-(id)init{
self = [super init];
if (self) {
// Set Variables
_name = @"Kiran";
}
return self;
}
@end
Надеюсь, что приведенный выше код поможет вам.
Если вам нужно создать синглтон в Swift,
class var sharedInstance: MyClass {
struct Singleton {
static let instance = MyClass()
}
return Singleton.instance
}
или же
struct Singleton {
static let sharedInstance = MyClass()
}
class var sharedInstance: MyClass {
return Singleton.sharedInstance
}
Вы можете использовать этот способ
let sharedClass = LibraryAPI.sharedInstance