为NSObject子类设置init()私有

Cas*_*sey 4 swift

该类FooClass应仅允许通过进行交互sharedInstance。我试图防止滥用不容许任何人访问init()FooClass

我尝试了几种不同的方法,但是没有用:

使用私人关键字:

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

使用@available:

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

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

更新

如果将第一个版本移到其自己的文件中,则可以使用第一个版本,但是该类的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
Run Code Online (Sandbox Code Playgroud)

Col*_*aff 5

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


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

但是,正如您所说,在Objective-C中使用Swift类将删除private为您提供的保护。我相信这是因为任何标记的东西private在编译器生成的导入头文件中都没有条目。因此,init继承自的功能NSObject是可用的,因为它不会被覆盖。

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

迅捷类:

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

  let value: String

  private override init() {
    self.value = "asdf"
  }
}
Run Code Online (Sandbox Code Playgroud)

Objective-C标头:

@interface FooClass (SharedInstance)
+ (instancetype) new  NS_UNAVAILABLE;
- (instancetype) init NS_UNAVAILABLE;
@end
Run Code Online (Sandbox Code Playgroud)

您还必须阻止,new因为如果不这样做,则可以通过该类创建该类的实例。

测试:

FooClass* foo  = [FooClass sharedInstance]; // All good here
FooClass* foo2 = [[FooClass alloc] init];   // 'init' is unavailable
FooClass* foo3 = [FooClass new];            // 'new' is unavailable
Run Code Online (Sandbox Code Playgroud)

我在这里有一个简单的示例项目:SharedInstance项目git


Lud*_*dry 5

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

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