如何强制基于 NSObject 的基础 class 的子 class 覆盖 class 级变量?
How can you enforce a subclass of an NSObject-based base class override a class-level variable?
再次完全重写这个问题,希望将重点放在我试图解决的问题上。
我们有以下 class,由于我们无法控制的外部框架,需要我们实现 ObjCProtocolRequiringBla
,就像这样...
class FooBase : NSObject, ObjCProtocolRequiringBla {
class var possibleValues:[String] {
// Note: Use preconditionFailure, not fatalError for things like this.
// At runtime, they both halt execution, but precondition(Failure)
// only logs the message for debug builds. Additionally,
// in debug builds it pauses in a debuggable state rather than crash.
preconditionFailure("You must override possibleValues")
}
// This satisfies the objective-c protocol requirement
final func getBla() -> Bla {
let subclassSpecificValues = type(of:self).possibleValues
return Bla(withValues:subclassSpecificValues)
}
}
class FooA : FooBase
override class var possibleValues:[String] {
return [
"Value A1",
"Value A2",
"Value A3"
]
}
}
class FooB : FooBase
override class var possibleValues:[String] {
return [
"Value B1",
"Value B2",
"Value B3",
"Value B4"
]
}
}
可以看到,在getBla()
的实现中使用了possibleValues
。此外,它必须对特定子 class 的所有实例都是通用的,因此是 class 变量而不是实例变量。这是因为在代码的另一个地方,我们必须检索所有子 class 中的所有可能值,就像这样...
static func getAllPossibleValues:[String] {
return [
FooA.possibleValues,
FooB.possibleValues
].flatMap { [=13=] }
}
如果 FooBase
的子 class 没有实现 possibleValues
.
,我想弄清楚如何让编译器抱怨
换句话说,我们如何让这个报告编译时错误:
class FooC : FooBase
// Doesn't override class var possibleValues
}
目前我知道的唯一方法是上面的方法,在基础 class 中用 preconditionFailure
或类似的方法定义它,但这是运行时检查,而不是编译时,所以它不是最佳解决方案。
您可以通过将 FooBase class 替换为扩展 NSObjectProtocol 的协议来解决此问题。这将强制任何 class 实现你的协议必须是 NSObject 的子 class 并且还实现你想要的属性和方法。
protocol foo: NSObjectProtocol {
var someProp: String {get set}
func someFunc() -> Void
}
要使用来自 Objective-C 的协议,可以使用 @objc 注释,这至少适用于简单的场景,例如发送选择器 #selector() 作为参数。
@objc protocol foo: NSObjectProtocol {
var someProp: String {get set}
func someFunc() -> Void
}
下面的代码工作正常
class fooClass: NSObject, foo {
var someProp: String
init(someProp: String) {
self.someProp = someProp
}
func someFunc() {
print("In someFunc with property: \(someProp)")
}
func timeIt() {
let timer = Timer.scheduledTimer(timeInterval: 0,
target: self, selector: #selector(someFunc), userInfo: nil, repeats: false)
print("timer created")
timer.fire()
}
}
func main() {
let f = fooClass(someProp: "test")
f.timeIt()
}
main()
What I'm trying to figure out how to do is make the compiler complain if a subclass of FooBase does not implement possibleValues
.
你不能。显然,您可以将 possibleValues
之类的内容作为协议要求并尝试采用该协议,但是如果 FooBase 本身实现了 possibleValues
您就无法让 编译器 抱怨子类无法覆盖它。这不是一个愚蠢的想法,可能有一些计算机语言可以让你做到这一点,但 Swift 不是这样一种语言。这就是为什么运行时解决方案是您所希望的最好的解决方案,这就是标准模式,如果您查看类似 UIPopoverBackgroundView 的内容,就会看到这一点。
也许是这样的?我没有敲定每一个细节,但我认为这涵盖了你的很多编程问题。如果您想进一步讨论,让我们创建一个聊天室,而不是继续在这里 post 没完没了的评论。
// Just to get it to compile
class Bla { init(withValues: [String]) {} }
protocol ObjCProtocolRequiringBla {}
class FooContainer : ObjCProtocolRequiringBla {
fileprivate var fooCore: FooProtocol
fileprivate init(_ fooCore: FooProtocol) { self.fooCore = fooCore }
// This satisfies the objective-c protocol requirement
final func getBla() -> Bla { return fooCore.getBla() }
}
protocol FooProtocol {
static var possibleValues:[String] { get }
func getBla() -> Bla
}
class FooA : NSObject, FooProtocol {
class var possibleValues:[String] {
return [
"Value A1",
"Value A2",
"Value A3"
]
}
func getBla() -> Bla {
return Bla(withValues: FooA.possibleValues)
}
}
class FooB : NSObject, FooProtocol {
class var possibleValues:[String] {
return [
"Value B1",
"Value B2",
"Value B3",
"Value B4"
]
}
func getBla() -> Bla {
return Bla(withValues: FooB.possibleValues)
}
}
再次完全重写这个问题,希望将重点放在我试图解决的问题上。
我们有以下 class,由于我们无法控制的外部框架,需要我们实现 ObjCProtocolRequiringBla
,就像这样...
class FooBase : NSObject, ObjCProtocolRequiringBla {
class var possibleValues:[String] {
// Note: Use preconditionFailure, not fatalError for things like this.
// At runtime, they both halt execution, but precondition(Failure)
// only logs the message for debug builds. Additionally,
// in debug builds it pauses in a debuggable state rather than crash.
preconditionFailure("You must override possibleValues")
}
// This satisfies the objective-c protocol requirement
final func getBla() -> Bla {
let subclassSpecificValues = type(of:self).possibleValues
return Bla(withValues:subclassSpecificValues)
}
}
class FooA : FooBase
override class var possibleValues:[String] {
return [
"Value A1",
"Value A2",
"Value A3"
]
}
}
class FooB : FooBase
override class var possibleValues:[String] {
return [
"Value B1",
"Value B2",
"Value B3",
"Value B4"
]
}
}
可以看到,在getBla()
的实现中使用了possibleValues
。此外,它必须对特定子 class 的所有实例都是通用的,因此是 class 变量而不是实例变量。这是因为在代码的另一个地方,我们必须检索所有子 class 中的所有可能值,就像这样...
static func getAllPossibleValues:[String] {
return [
FooA.possibleValues,
FooB.possibleValues
].flatMap { [=13=] }
}
如果 FooBase
的子 class 没有实现 possibleValues
.
换句话说,我们如何让这个报告编译时错误:
class FooC : FooBase
// Doesn't override class var possibleValues
}
目前我知道的唯一方法是上面的方法,在基础 class 中用 preconditionFailure
或类似的方法定义它,但这是运行时检查,而不是编译时,所以它不是最佳解决方案。
您可以通过将 FooBase class 替换为扩展 NSObjectProtocol 的协议来解决此问题。这将强制任何 class 实现你的协议必须是 NSObject 的子 class 并且还实现你想要的属性和方法。
protocol foo: NSObjectProtocol {
var someProp: String {get set}
func someFunc() -> Void
}
要使用来自 Objective-C 的协议,可以使用 @objc 注释,这至少适用于简单的场景,例如发送选择器 #selector() 作为参数。
@objc protocol foo: NSObjectProtocol {
var someProp: String {get set}
func someFunc() -> Void
}
下面的代码工作正常
class fooClass: NSObject, foo {
var someProp: String
init(someProp: String) {
self.someProp = someProp
}
func someFunc() {
print("In someFunc with property: \(someProp)")
}
func timeIt() {
let timer = Timer.scheduledTimer(timeInterval: 0,
target: self, selector: #selector(someFunc), userInfo: nil, repeats: false)
print("timer created")
timer.fire()
}
}
func main() {
let f = fooClass(someProp: "test")
f.timeIt()
}
main()
What I'm trying to figure out how to do is make the compiler complain if a subclass of FooBase does not implement
possibleValues
.
你不能。显然,您可以将 possibleValues
之类的内容作为协议要求并尝试采用该协议,但是如果 FooBase 本身实现了 possibleValues
您就无法让 编译器 抱怨子类无法覆盖它。这不是一个愚蠢的想法,可能有一些计算机语言可以让你做到这一点,但 Swift 不是这样一种语言。这就是为什么运行时解决方案是您所希望的最好的解决方案,这就是标准模式,如果您查看类似 UIPopoverBackgroundView 的内容,就会看到这一点。
也许是这样的?我没有敲定每一个细节,但我认为这涵盖了你的很多编程问题。如果您想进一步讨论,让我们创建一个聊天室,而不是继续在这里 post 没完没了的评论。
// Just to get it to compile
class Bla { init(withValues: [String]) {} }
protocol ObjCProtocolRequiringBla {}
class FooContainer : ObjCProtocolRequiringBla {
fileprivate var fooCore: FooProtocol
fileprivate init(_ fooCore: FooProtocol) { self.fooCore = fooCore }
// This satisfies the objective-c protocol requirement
final func getBla() -> Bla { return fooCore.getBla() }
}
protocol FooProtocol {
static var possibleValues:[String] { get }
func getBla() -> Bla
}
class FooA : NSObject, FooProtocol {
class var possibleValues:[String] {
return [
"Value A1",
"Value A2",
"Value A3"
]
}
func getBla() -> Bla {
return Bla(withValues: FooA.possibleValues)
}
}
class FooB : NSObject, FooProtocol {
class var possibleValues:[String] {
return [
"Value B1",
"Value B2",
"Value B3",
"Value B4"
]
}
func getBla() -> Bla {
return Bla(withValues: FooB.possibleValues)
}
}