Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - How can I override an extension method in a concrete subclass

I have an extension on UIView implementing a protocol

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

in a concrete subclass I want to override this extension method:

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

I set breakpoints and see that the extension method is called and not the concrete subclass method:

var subclassObject = Subclass()

someObject.doSomethingWithConcreteSubclassObject(subclassObject)

// other code;

fun doSomethingWithConcreteSuclassObject(object : UIView) {
  var value = object.property // always goes to extension class get/set
}
like image 550
Avner Barr Avatar asked Apr 27 '15 10:04

Avner Barr


People also ask

Can we override extension method Swift?

If a protocol defines a method, and provides a default implementation in an extension, a class can override that method, and the override will be called for any reference to the instance, even if the reference's type is declared as the protocol.

Can we override extension method?

You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called.

Can we override protocol in Swift?

Here's the problem comes. An extension can't be overridden because the way Swift implement extension is using static dispatch which means its resolved at compile time.


1 Answers

As others have noted, Swift does not (yet) allow you to override a method declared in a class extension. However, I'm not sure whether you'll ever get the behavior you want even if/when Swift someday allows you to override these methods.

Consider how Swift deals with protocols and protocol extensions. Given a protocol to print some metasyntactic variable names:

protocol Metasyntactic {
    func foo() -> String
    func bar() -> String
}

An extension to provide default implementations:

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

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

And a class that conforms to the protocol:

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

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

Swift will use dynamic dispatch to call the appropriate implementations of foo() and bar() based on each variable's runtime type rather than on the type inferred by the compiler:

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

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

If, however, we extend the protocol further to add a new method:

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

And if we override our new method in a class that conforms to the protocol:

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

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

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

Swift will now use static dispatch to call the appropriate implementation of baz() based on the type inferred by the compiler:

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

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

Alexandros Salazar has a fantastic blog post explaining this behavior in depth, but suffice it to say that Swift only uses dynamic dispatch for methods declared in the original protocol, not for methods declared in protocol extensions. I imagine the same would be true of class extensions, as well.

like image 170
Chris Frederick Avatar answered Nov 13 '22 10:11

Chris Frederick