web-dev-qa-db-ja.com

Swift-具象サブクラスで拡張メソッドをオーバーライドするにはどうすればよいですか?

プロトコルを実装するUIViewの拡張機能があります

protocol SomeProtocol {
  var property : Int
}
    extension UIView : SomeProtocol {
      var property : Int {
        get {
          return 0
        }
        set {
          // do nothing
        }
      }
    }

具体的なサブクラスで、この拡張メソッドをオーバーライドします。

class Subclass : UIView, SomeProtocol {
  var _property : Int = 1
  var property : Int {
    get { return _property}
    set(val) {_property = val}
  }
}

ブレークポイントを設定し、具象サブクラスメソッドではなく拡張メソッドが呼び出されることを確認します。

var subclassObject = Subclass()

someObject.doSomethingWithConcreteSubclassObject(subclassObject)

// other code;

fun doSomethingWithConcreteSuclassObject(object : UIView) {
  var value = object.property // always goes to extension class get/set
}
18
Avner Barr

他の人が指摘したように 、Swiftでは、クラス拡張で宣言されているメソッドをオーバーライドすることは(まだ)できません。しかし、 Swiftいつかこれらのメソッドをオーバーライドできるようになった場合でも、必要な動作が得られます。

Swiftがプロトコルとプロトコル拡張をどのように扱うかを考えてください。いくつかのメタ構文変数名を出力するプロトコルが与えられた場合:

_protocol Metasyntactic {
    func foo() -> String
    func bar() -> String
}
_

デフォルトの実装を提供する拡張機能:

_extension Metasyntactic {
    func foo() -> String {
        return "foo"
    }

    func bar() -> String {
        return "bar"
    }
}
_

そして、プロトコルに準拠するクラス:

_class FooBar : Metasyntactic {
    func foo() -> String {
        return "FOO"
    }

    func bar() -> String {
        return "BAR"
    }
}
_

Swiftはdynamic dispatchを使用して、各変数のランタイムに基づいてfoo()およびbar()の適切な実装を呼び出しますコンパイラによって推論された型ではなくtype

_let a = FooBar()
a.foo()  // Prints "FOO"
a.bar()  // Prints "BAR"

let b: Metasyntactic = FooBar()
b.foo()  // Prints "FOO"
b.bar()  // Prints "BAR"
_

ただし、プロトコルをさらに拡張して新しいメソッドを追加する場合:

_extension Metasyntactic {
    func baz() -> String {
        return "baz"
    }
}
_

そして、プロトコルに準拠するクラスで新しいメソッドをオーバーライドする場合:

_class FooBarBaz : Metasyntactic {
    func foo() -> String {
        return "FOO"
    }

    func bar() -> String {
        return "BAR"
    }

    func baz() -> String {
        return "BAZ"
    }
}
_

Swiftはstatic dispatchを使用して、コンパイラーによって推論された型に基づいてbaz()の適切な実装を呼び出します。

_let a = FooBarBaz()
a.baz()  // Prints "BAZ"

let b: Metasyntactic = FooBarBaz()
b.baz()  // Prints "baz"
_

Alexandros Salazarは この振る舞いを詳しく説明している素晴らしいブログ記事 ですが、Swiftは元のプロトコルで宣言されたメソッドに動的ディスパッチのみを使用します、notプロトコル拡張で宣言されたメソッドの場合、クラス拡張でも同じことが当てはまると思います。

15
Chris Frederick

私はこの質問が少し前に尋ねられたことを知っています。しかし、これはより簡単な方法を探している人にとっては便利です。拡張メソッドをオーバーライドする方法があります。私はその少しハックを知っていますが、それは美しく仕事をします。

プロトコルを@objcで宣言した場合

@objc protocol MethodOverridable {
   func overrideMe()
}

拡張内

extension MainClass: MethodOverridable {
     func overrideMe() {
        print("Something useful")
     } 
}

サブクラス-サブクラスでオーバーライドできます。それは魔法のように機能します。まあ、実際には@objcを追加するときではなく、protocol to Objective-CとそのRuntimeを公開します。これにより、サブクラスでオーバーライドできます。

 class SubClass: MainClass {
     override func overrideMe() {
          print("Something more useful")
      }  
  }
0
vinothp

2番目のスーパークラスプロパティのプロパティをオーバーライドできるようです。たとえば、UIViewプロパティにアクセスするには、UILabelを拡張して、frameUIViewプロパティをオーバーライドします。このサンプルはXcode 6.3.2で動作します

extension UILabel {

     override public var frame: CGRect {

         didSet {
             println("\(frame)")
        }
     }
}
0
vedrano