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

Ver*_*erd 6 singleton objective-c ios

我知道这有几个主题,但没有人回答我的问题.

我已经像这样实现了我的单例类(了解有关单身人士的争议):

+ (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;
}
Run Code Online (Sandbox Code Playgroud)

我尝试实例化一个不同的对象,并将它与sharedInstance返回的'=='进行比较,它们确实不同.

问题:

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

CRD*_*CRD 13

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

在旧的MRC时代,Apple曾经有过示例代码,展示了如何实现真正的单例.

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

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

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

这就是你需要做的,但你可以走得更远.首先,如果您希望有一个类方法来返回单例,那么它就是:

+ (instancetype) singletonInstance
{
   return [self new];
}
Run Code Online (Sandbox Code Playgroud)

此方法最终调用init返回单例,并在需要时创建它.

如果是MyClassimplements,NSCopying那么你还需要实现copyWithZone:- 这是copy调用的方法.你有一个单身人士,这很简单:

- (instancetype) copyWithZone:(NSZone *)zone
{
   return self;
}
Run Code Online (Sandbox Code Playgroud)

最后,在Objective-C中,分配新对象实例并对其进行初始化的操作是不同的.上述方案确保只MyClass初始化和使用一个实例,但是每次调用newalloc另一个实例都被分配,然后init由ARC 迅速丢弃并清理.这有点浪费!

这可以通过实现allocWithZone:(copy如上所述方法alloc实际上最终调用)通过以下相同模式实现init:

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

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

就是这样,一个真正的单身人士,并没有比如此常见的人造单身人士更难.

HTH


Glo*_*del 5

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

你可以做什么:让init方法引发一个异常(例如with [self doesNotRecognizeSelector:@_cmd])并以不同的方法(例如privateInit)执行初始化,该方法未在头文件中公开.


Him*_*jan 5

使用objective-c,您可以防止您的单例类创建多个对象。您可以使用单例类阻止 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
Run Code Online (Sandbox Code Playgroud)

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")));
Run Code Online (Sandbox Code Playgroud)

只需使用此代码为单例模式创建单个对象,并防止来自其他类的单例模式的 alloc init 调用。我已经用 xCode 7.0+ 测试过这段代码并且它工作正常。