Swift -- 要求 classes 实现协议是某个 class 的子classes

Swift -- Require classes implementing protocol to be subclasses of a certain class

我正在创建几个 NSView 类,所有这些类都支持一个特殊的操作,我们称之为 transmogrify。乍一看,这似乎是协议的完美位置:

protocol TransmogrifiableView {
    func transmogrify()
}

但是,此协议不会强制每个 TransmogrifiableView 也是 NSView。这意味着我在 TransmogrifiableView 上调用的任何 NSView 方法都不会类型检查:

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView // error: TransmogrifiableView does not have a property called 'superview'

我不知道如何要求所有实现我的协议的类也是 NSView 的子类。我试过这个:

protocol TransmogrifiableView: NSView {
    func transmogrify()
}

但是 Swift 抱怨协议不能从类继承。使用

将协议转换为仅类协议无济于事
protocol TransmogrifiableView: class, NSView {
    func transmogrify()
}

我不能让 TransmogrifiableView 成为超类而不是协议,因为我的某些 TransmogrifiableView 类必须是其他不可变形视图的子类。

我应该如何要求所有 TransmogrifiableView 也都是 NSView?我真的不想在我的代码中加入“as”转换,这是一种糟糕的形式并且会分散注意力。

我认为您是在 NSView 的子 class 之后。试试这个:

protocol TransmogrifiableView {
    func transmogrify()
}

class MyNSView: NSView, TransmogrifiableView {
    // do stuff.
}

稍后在代码中接受类型为 MyNSView 的对象。

编辑

您可能想要 Extension,请参阅 this

extension NSView: TransmogrifiableView {
    // implementation of protocol requirements goes here
}
  • 请注意,如果没有这个额外的方法,您将无法获得 NSView。
  • 您可以单独扩展 NSView 的子classes 来覆盖这个新方法。

另一种选择是制作一个 class ,它包含一个指向 NSView 的指针,并实现其他方法。这也将强制您代理 all 您想要使用的 NSView 方法。

class NSViewWrapper: TransmogrifiableView {
    var view : NSView!
    // init with the view required.
    //  implementation of protocol requirements goes here.
    .....
   // proxy all methods from NSView.
   func getSuperView(){
       return self.view.superView
   }
}

这篇文章很长而且不太好,但是可以用。我建议你只有在你真的不能使用扩展时才使用它(因为你需要 NSViews 而没有额外的方法)。

根据定义,协议仅声明 "methods, properties an other requirements" 的要求。 "other requirements" 表示超类不是它的一部分。

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.

目前,我没有看到一个干净的解决方案。可以使用 where 子句来定义类型 NSView 并符合 TransmogrifiableView ,如下所示:

class MyClass<T where T: NSView, T: TransmogrifiableView> {
    var aTransmogrifiableNSView: T
}

或者您可以使用另一个超类:

protocol TransmogrifiableViewProtocol {
    func transmogrify()
}

class TransmogrifiableView: NSView, TransmogrifiableViewProtocol {
    func transmogrify() {
        assert(false, "transmogrify() must be overwritten!")
    }
}

class AnImplementedTransmogrifiableView: TransmogrifiableView {
    func transmogrify() {
        println("Do the transmogrification...")
    }
}

最后两种解决方案都不干净,也不会让我满意。也许有一天 abstract 关键字会添加到 Swift?

仍然不是理想的解决方案,但这是我偶尔使用的模式:

  1. 使用您要强制执行的方法定义协议。
  2. 定义您的基础 class,实施您希望 children 免费获得的任何内容。
  3. 在该基础 class(来自 #2)中,有一个可选的 "delegate" 您在 #1 中创建的协议变量。
  4. 定义所有 children classes 以便它们继承基础 class 并实现协议。

这让您的基础 class 调用协议方法,同时强制 children 实现它们(例如 self.myDelegate?.myProtocolMethod)。

更新。在最新的Swift版本中你可以写

protocol TransmogrifiableView: NSView {
    func transmogrify()
}

,这将强制符合者类型为 NSView 或其子类。这意味着编译器将“看到”NSView.

的所有成员

原回答

通过使用关联类型强制执行子类有一个解决方法:

protocol TransmogrifiableView {
    associatedtype View: NSView = Self
    func transmogrify()
}

class MyView: NSView, TransmogrifiableView { ... } // compiles
class MyOtherClass: TransmogrifiableView { ... } // doesn't compile

从 Swift 4 开始,您现在可以定义如下:

let myView: NSView & TransmogrifiableView

更多信息,结帐问题#156 Subclass Existentials

你可以使用这样的东西:

protocol TransmogrifiableView where Self:NSView {}

这要求所有创建的符合 TransmogrifiableView 协议的实例都被 NSView 子类化

对于Swift 4,基于@Antoine 敏锐的洞察力:

创建协议,然后使用类型别名为同时符合 class 和协议的类型提供更清晰的名称。

protocol Transmogrifiable {
    func transmogrify()
}
typealias TransmogrifiableView = NSView & Transmogrifiable

然后您可以定义从该类型继承的 class....

class ATransmogView: TransmogrifiableView {
    func transmogrify() {
        print("I'm transmogging")
    }
}

.....或者定义一个继承自协议的class和一个subclass

// this also qualifies as a TransmogrifiableView

class BTransmogView: NSTextView, Transmogrifiable {
    func transmogrify() {
        print("I'm transmogging too")
    }
}

现在你可以做到了。

func getTransmogrifiableView() -> TransmogrifiableView {
    return someBool ? ATransmogView() : BTransmogView()
}

现在可以编译了。

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView

在此处检查此解决方案... Swift - class 方法必须被 subclass 覆盖

此解决方案允许从 class 继承并进行协议的编译时检查。 :)