如何强制基于NSObject的基类的子类覆盖类级变量?

时间:2018-03-17 20:20:34

标签: swift abstract-class swift4 swift-protocols

再次完全重写这个问题,希望把重点放在我试图解决的问题上。

由于我们无法控制的外部框架,我们有以下课程要求我们实施"(?s:.*?)(?<!\\)" ,如此...

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" ] } } 用于实施possibleValues。此外,它必须与该特定子类的所有实例相同,因此是一个类变量而不是实例变量。这是因为在代码中的另一个地方,我们必须检索所有子类中的所有可能值,例如......

getBla()

如果static func getAllPossibleValues:[String] { return [ FooA.possibleValues, FooB.possibleValues ].flatMap { $0 } } 的子类未实现FooBase,我试图弄清楚如何做是让编译器抱怨。

换句话说,我们如何才能使此报告成为编译时错误:

possibleValues

目前我所知道的唯一方法是上面的方法,在基类中用class FooC : FooBase // Doesn't override class var possibleValues } 或类似方法定义它,但这是运行时检查,而不是编译时间,所以它不是最好的解决方案。

3 个答案:

答案 0 :(得分:0)

您可以通过使用扩展NSObjectProtocol的协议替换您的FooBase类来解决此问题。这将强制任何实现协议的类必须是NSObject的子类,并且还要实现所需的属性和方法。

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

答案 1 :(得分:0)

  

我试图弄清楚如何处理,如果FooBase的子类没有实现possibleValues,编译器就会抱怨。

你不能。您可以使possibleValues类似于协议要求,并尝试采用该协议,但是如果FooBase本身实现possibleValues,如果子类失败,则无法让编译器抱怨覆盖它。这不是一个愚蠢的想法,可能有计算机语言为你提供了一种方法,但Swift并不是一种语言。这就是为什么运行时解决方案是你能想到的最好的原因,而且这是标准模式,正如你看到UIPopoverBackgroundView这样的东西你可以看到。

答案 2 :(得分:0)

也许是这些方面的东西?我没有敲定每一个细节,但我认为这涵盖了很多你的编程问题。如果你想进一步讨论,让我们开一个聊天室,而不是继续在这里发表无休止的评论。

// 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)
    }
}