web-dev-qa-db-ja.com

Swift-特定のクラスのサブクラスであるプロトコルを実装するクラスを要求する

いくつかのNSViewクラスを作成していますが、それらはすべてtransmogrifyと呼ばれる特別な操作をサポートしています。一見、これはプロトコルに最適な場所のようです。

protocol TransmogrifiableView {
    func transmogrify()
}

ただし、このプロトコルはnotではなく、すべてのTransmogrifiableViewNSViewであることを強制します。これは、NSViewで呼び出すTransmogrifiableViewメソッドはタイプチェックを行わないことを意味します。

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クラスの一部は、変換できない他のビューのサブクラスでなければならないからです。

すべてのTransmogrifiableViewNSViewであることをどのように要求すればよいですか? 「as」変換でコードを混乱させたくはありません。これは悪い形式であり、気を散らすものです。

37
SelectricSimian

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

これはかなり長く、ニースではありませんが、機能します。拡張機能を実際に使用できない場合にのみ、これを使用することをお勧めします(追加のメソッドなしでNSViewsが必要なため)。

5
Ramzi Khahil

Swift 4から開始すると、次のように定義できます。

let myView: NSView & TransmogrifiableView

詳細については、チェックアウトの問題 #156 Subclass Existentials

17
Antoine

関連付けられた型を使用してサブクラスを強制することにより、回避策があります。

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

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

次のようなものを使用できます。

protocol TransmogrifiableView where Self:NSView {}

これには、TransmogrifiableViewプロトコルに準拠するすべての作成済みインスタンスをNSViewでサブクラス化する必要があります

10
GrandFelix

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
3
pinch

定義により、プロトコルは「メソッド、プロパティ、およびその他の要件」の要件を宣言するだけです。 「その他の要件」とは、スーパークラスがその一部ではないことを意味します。

プロトコルは、特定のタスクまたは機能に適したメソッド、プロパティ、およびその他の要件の青写真を定義します。

今のところ、きれいな解決策は見当たりません。 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- keywordがSwiftいつか?

3
Fabio Poloni

ここでこの解決策を確認してください... Swift-サブクラスでオーバーライドする必要があるクラスメソッド

このソリューションにより、クラスを継承し、プロトコルのコンパイル時チェックを行うことができます。 :)

0
JMiguel

それでも理想的な解決策ではありませんが、私が時々使用するパターンは次のとおりです。

  1. 実施するメソッドを使用してプロトコルを定義します。
  2. 基本クラスを定義し、子が無料で取得できるものを実装します。
  3. その基本クラス(#2から)に、#1で作成したプロトコルのオプションの「デリゲート」変数を用意します。
  4. 基本クラスを継承してプロトコルを実装するように、すべての子クラスを定義します。

これにより、ベースクラスがプロトコルメソッドを呼び出して、子に強制的に実装させます(例:self.myDelegate?.myProtocolMethod)。

0
ghostatron