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变量?