iOS中的单例Objective C不会阻止多个实例

我知道有几个线程,但没有回答我的问题。

我已经实现了像这样的单身类(意识到关于单身人士的争议):

+ (MyClass*) sharedInstance { static MyClass *_sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedInstance = [[MyClass alloc] init]; }); return _sharedInstance; } - (instancetype)init{ self = [super init]; if (self) { //setup code } return self; } 

我试着实例化一个不同的对象,并用'=='与sharedInstance返回的对象进行比较,确实不同。

问题:

  1. 不应该创build单个类的多个对象是不可能的? 这不是重点吗? Java中的单例实现可以防止它。
  2. 如果是的话,怎么样? 我应该做一个设置方法,并调用它,而不是执行init和做?
  3. 这是正确的实施?

您的观察是正确的,您在Objective-C中看到的许多“单例”模式根本不是单例,而是可以创build其他实例的“共享实例”模型。

在过去的MRC时代,Apple曾经有示例代码展示了如何实现一个真正的单例。

您所拥有的代码是ARC和线程安全单例的推荐模式,您只需将其放在init方法中:

 - (instancetype) init { static MyClass *initedObject; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ initedObject = [super init]; }); return initedObject; } 

无论有多less[MyClass new][[MyClass alloc] init]调用,此代码将确保只有一个MyClass实例。

这就是你需要做的,但你可以走得更远。 首先,如果你希望有一个类方法来返回单身人士,它只是简单的:

 + (instancetype) singletonInstance { return [self new]; } 

这种方法最终调用init返回单例,如果需要的话创build它。

如果MyClass实现NSCopying那么你还需要实现copyWithZone: – 这是copy调用的方法。 就像你是一个单身人士,这很简单:

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

最后在Objective-C中,分配一个新的对象实例并初始化它的操作是截然不同的。 上面的scheme确保只有一个MyClass实例被初始化和使用,但是对于每次调用newalloc另一个实例被分配,然后立即被init抛弃并由ARC清理。 这有点浪费!

这很容易通过实现allocWithZone:像上面这样的方法是alloc实际上最终调用的方法)遵循与init相同的模式:

 + (instancetype) allocWithZone:(NSZone *)zone { static MyClass *allocatedObject; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ allocatedObject = [super allocWithZone:zone]; }); return allocatedObject; } 

第一次创build实例时, allocWithZone:将分配它,然后init将初始化它,所有后续调用将返回已经存在的对象。 没有丢弃不需要的分配。

就是这样,一个真正的单身人士,并不比那些常见的人造单身汉更难。

HTH

你不能让init方法是私有的,就像你在Java中使用构造函数一样。 所以没有什么能阻止你调用[[MyClass alloc] init] ,这确实创build了一个不同的对象。 只要你不这样做,但坚持sharedInstance方法,你的实现是好的。

你可以做些什么:让init方法引发一个exception(例如,使用[self doesNotRecognizeSelector:@_cmd] ),并用另一个不在头文件中公开的方法(例如privateInit )执行初始化。

为了防止创build单个类的多个对象,您需要执行以下操作。 你可以很好地创build单身人士的对象。 但在调用init,copy,mutable copy时,需要处理这种方式。

 - (instancetype)init{ if (!_sharedInstance) { _sharedInstance = [MyClass sharedInstance]; } return _sharedInstance; } - (id)copy{ if (!_sharedInstance) { _sharedInstance = [MyClass sharedInstance]; } return _sharedInstance; } 

对于可变拷贝也是一样的东西。 所以这个实现确保一旦有一个实例在整个可用

愿这帮助你。

使用objective-c,你可以防止你的单例类创build多个对象。 你可以防止你的单身类的alloc和init调用。

 #import <Foundation/Foundation.h> @interface SingletonClass : NSObject + (id) sharedInstance; - (void) someMethodCall; - (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead"))); + (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead"))); @end #import "SingletonClass.h" @implementation SingletonClass + (id) sharedInstance{ static SingletonClass * sharedObject = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedObject = [[self alloc] initPrivate]; }); return sharedObject; } - (instancetype)init { @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"You can't override the init call in class %@", NSStringFromClass([self class])] userInfo:nil]; } - (instancetype)initPrivate { if (self = [super init]) { } return self; } - (void) someMethodCall{ NSLog(@"Method Call"); } @end 

1#如果您将尝试在SingletonClass上调用init或new方法,则这些方法将无法调用。

2#如果你注释掉下面提到的头文件中的方法,并尝试调用SingletonClass方法的init,那么应用程序将会崩溃,原因是“你不能在类SingletonClass中覆盖init调用”。

  - (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead"))); + (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead"))); 

只需使用此代码创build单个对象到Singleton模式,并防止其他类的单一模式的alloc init调用。 我已经用xCode 7.0+testing了这个代码,它的工作正常。

@VeryPoliteNerd只是将.hinit方法标记为不可用.h

 - (instancetype)init __attribute__((unavailable("Use +[MyClass sharedInstance] instead"))); + (instancetype)new __attribute__((unavailable("Use +[MyClass sharedInstance] instead"))); 

如果调用者试图手动实例化这个对象,这将导致编译器抱怨

调用alloc / init来获得一个Singleton类的第二个实例被认为是一个公然的编程错误。 为了避免这种编程错误,您不要编写复杂的代码来防止这种错误,您可以执行代码审查,并告诉所有尝试执行此操作的人员,就像您遇到任何编程错误一样。

这适用于我:

 static AudioRecordingGraph * __strong sharedInstance; +(instancetype)sharedInstance { @synchronized(self) { if(!sharedInstance) { sharedInstance = [AudioRecordingGraph new]; } return sharedInstance; } } 

这适用于我:

 static DataModel *singleInstance; + (DataModel*)getInstance{ if (singleInstance == nil) { singleInstance = [[super alloc] init]; } return singleInstance; } 

你可以用它来调用它

 _model = [DataModel getInstance];