使init()为NSObject子类私有

时间:2016-02-03 00:57:56

标签: swift

班级FooClass应该只允许通过其sharedInstance进行互动。我试图通过不允许任何人访问init()的{​​{1}}来防止滥用。

我尝试过几种不同的方法,但都没有效果:

使用私人关键字:

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()

我也试过使用内部,但仍然没有运气。

更新

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

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()

2 个答案:

答案 0 :(得分:5)

This answer addresses Swift 2. In Swift 3 it looks like the access level of a method is correctly imported from Swift to Objective-C and does not need to be marked as NS_UNAVAILABLE in order to disallow it from being available. Of course, there is no new method in Swift so that will still need to be marked as NS_UNAVAILABLE to maintain the singleton properly.


Your first approach will work as long as the class is put in its own file. The access control keyword private means that the defined feature will only be available within the containing file.

However, as you said, using the Swift class in Objective-C will remove the protection that private gives you. I believe that's because anything marked private will not have an entry in the imported header file generated by the compiler. Thus the init function inherited from NSObject is available because it isn't overridden.

The solution I found is to create another header file that explicitly declares an init function that can't be called.

Swift class:

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

  let value: String

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

Objective-C header:

@interface FooClass (SharedInstance)
+ (instancetype) new  NS_UNAVAILABLE;
- (instancetype) init NS_UNAVAILABLE;
@end

You have to also block new because if you don't then an instance of the class could be created through it.

Testing:

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

I have a rudimentary example project here: SharedInstance project git

答案 1 :(得分:3)

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

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