Swift 使NSObject子类的init()私有

Swift 使NSObject子类的init()私有,swift,Swift,类FooClass应该只允许通过其sharedInstance进行交互。我试图通过不允许任何人访问FooClass的init()来防止误用 我尝试过几种不同的方法,但都不管用: 使用私有关键字: class FooClass: NSObject { // singleton static let sharedInstance = FooClass() let value: String private override init() { sel

FooClass
应该只允许通过其
sharedInstance
进行交互。我试图通过不允许任何人访问
FooClass
init()
来防止误用

我尝试过几种不同的方法,但都不管用:

使用私有关键字:

class FooClass: NSObject {
    // singleton
    static let sharedInstance = FooClass()

    let value: String

    private override init() {
        self.value = "asdf"
    }
}

// this should be a compile error, but it is not
let foo = FooClass()
class FooClass: NSObject {
    // singleton
    // COMPILE ERROR - "init is unavailable. use sharedInstance"
    static let sharedInstance = FooClass()

    let value: String

    @available(*, unavailable, message="use sharedInstance")
    override init() {
        self.value = "asdf"
    }
}

// COMPILE ERROR - as it should be
let foo = FooClass()
使用@available:

class FooClass: NSObject {
    // singleton
    static let sharedInstance = FooClass()

    let value: String

    private override init() {
        self.value = "asdf"
    }
}

// this should be a compile error, but it is not
let foo = FooClass()
class FooClass: NSObject {
    // singleton
    // COMPILE ERROR - "init is unavailable. use sharedInstance"
    static let sharedInstance = FooClass()

    let value: String

    @available(*, unavailable, message="use sharedInstance")
    override init() {
        self.value = "asdf"
    }
}

// COMPILE ERROR - as it should be
let foo = FooClass()
我也尝试过使用内部的,但仍然没有运气

更新

如果将第一个版本移动到它自己的文件中,那么它就可以工作,但是该类的ObjC版本仍然允许调用init。有什么想法吗

SWIFT_CLASS("_TtC11SwiftToObjC8FooClass")
@interface FooClass : NSObject
+ (FooClass * __nonnull)sharedInstance;
@property (nonatomic, readonly, copy) NSString * __nonnull value;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

这个答案是针对Swift 2的。在Swift 3中,方法的访问级别似乎已从Swift正确导入到Objective-C,不需要标记为
NS\u UNAVAILABLE
,以禁止其可用。当然,Swift中没有新的
方法,因此仍然需要将其标记为
NS\u UNAVAILABLE
,以正确维护单例


只要类放在自己的文件中,第一种方法就可以工作。访问控制关键字
private
表示定义的功能仅在包含的文件中可用

但是,正如您所说,在Objective-C中使用Swift类将删除
private
为您提供的保护。我认为这是因为任何标记为
private
的内容在编译器生成的导入头文件中都不会有条目。因此,从
NSObject
继承的
init
函数可用,因为它没有被重写

我找到的解决方案是创建另一个头文件,该头文件显式声明无法调用的
init
函数

Swift等级:

@objc(FooClass)
class FooClass: NSObject {
  // singleton
  static let sharedInstance = FooClass()

  let value: String

  private override init() {
    self.value = "asdf"
  }
}
目标-C标题:

@interface FooClass (SharedInstance)
+ (instancetype) new  NS_UNAVAILABLE;
- (instancetype) init NS_UNAVAILABLE;
@end
您还必须阻止
new
,因为如果不阻止,则可以通过它创建类的实例

测试:

FooClass* foo  = [FooClass sharedInstance]; // All good here
FooClass* foo2 = [[FooClass alloc] init];   // 'init' is unavailable
FooClass* foo3 = [FooClass new];            // 'new' is unavailable

我这里有一个基本的示例项目:

您可以使用
@available()
注释:

@available(*, unavailable, message: "Use __your_init__ instead")
override init() {
    fatalError("init() has not been implemented")
}

当你回答时,我当然意识到了这一点!那完全正确;DR:使用第一个实现,但将其移动到自己的文件中。一个问题是,这个类的ObjC版本仍然允许您使用init。知道怎么阻止吗?是的,就像你说的。我在一个混合的Swift/Objective-C项目中尝试了它,而
private init()
对于Objective-C代码来说并不重要。不知道如何修复这一方面,它在Swift方面工作得很好。您可以将其添加到类
@接口下生成的标题“ProjectModuleName Swift.h”:
-(\u Nonnull instancetype)init NS\u UNAVAILABLE我不知道如何自动生成。这显然是Swift 4+的正确答案-谢谢!奇怪的是,您必须拥有fatalError(),否则它将无法生成,即使宏使init不可用。无论如何,编译时错误是好的!它标记了一个在我没有@available宏时丢失的测试用例!如何使用这种方法将类的实例分配给sharedInstance变量?