首页 文章

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

提问于
浏览
37

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

protocol TransmogrifiableView {
    func transmogrify()
}

但是,此协议并未强制每个 TransmogrifiableView 也是 NSView . 这意味着我在 TransmogrifiableView 上调用的任何 NSView 方法都不会键入check:

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

I cannot make TransmogrifiableView a superclass rather than a protocol, because some of my TransmogrifiableView classes must be subclasses of other, non-transmogrifiable views.

我应该如何要求所有 TransmogrifiableView 也要 NSView 's? I really don'想要用“ as ”转换来加密我的代码,这些转换形式不好而且分散注意力 .

8 回答

  • 16

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

    创建协议然后使用typealias为符合类和协议的类型提供更清晰的名称 .

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

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

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

    ....或者定义一个继承自协议和子类的类

    // 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
    
  • 17

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

    • 使用要强制执行的方法定义协议 .

    • 定义您的基类,实现您希望孩子们免费获得的任何内容 .

    • 在该基类(来自#2)中,有一个在#1中创建的协议的可选"delegate"变量 .

    • 定义所有子类,以便它们继承基类并实现协议 .

    这让你的基类调用协议方法,同时强制孩子们实现它们(例如,self.myDelegate?.myProtocolMethod) .

  • 2

    根据定义,协议只声明了“方法,属性和其他要求”的要求,这意味着超类不是它的一部分 .

    协议定义了适合特定任务或功能的方法,属性和其他要求的蓝图 .

    现在,我没有't see a clean solution. It'可以使用 where -clause定义一个类型 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 -keyword添加到Swift?

  • 3

    在这里检查这个解决方案... Swift - class method which must be overridden by subclass

    此解决方案允许从类继承并且还可以检查协议的编译时间 . :)

  • 8

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

    let myView: NSView & TransmogrifiableView
    

    有关更多信息,请结帐#156 Subclass Existentials

  • 0

    你可以使用这样的东西:

    protocol TransmogrifiableView where Self:NSView {}
    

    这要求所有创建的实例符合TransmogrifiableView协议,并使用NSView进行子类化

  • 0

    我想你是在 NSView 的子类之后 . 试试这个:

    protocol TransmogrifiableView {
        func transmogrify()
    }
    
    class MyNSView: NSView, TransmogrifiableView {
        // do stuff.
    }
    

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

    编辑

    你可能想要一个 Extension ,见this

    extension NSView: TransmogrifiableView {
        // implementation of protocol requirements goes here
    }
    
    • 请注意,如果没有这种额外的方法,您将无法获得NSView .

    • 您可以单独扩展NSView的子类以覆盖此新方法 .


    另一种选择是创建一个包含指向NSView的指针的类,并实现其他方法 . 这也将强制您从NSView中代理要使用的 all 方法 .

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

    这很长很不好,但会起作用 . 我建议你只在你真的无法使用扩展时才使用它(因为你需要NSView而不需要额外的方法) .

  • 4

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

    protocol TransmogrifiableView {
        associatedtype View: NSView = Self
        func transmogrify()
    }
    
    class MyView: NSView, TransmogrifiableView { ... } // compiles
    class MyOtherClass: TransmogrifiableView { ... } // doesn't compile
    

相关问题