How to force dynamic protocol dispatch?

I run into the issue of static protocol dispatch again and again it seems, usually doing anything around generic functionality with UIViewControllers (since they're so often subclassed). Very frustrated Swift user! :grimacing:

What are the options to finally fixing this language wart? Is there a mechanism to mark a protocol @dynamic and finally get intuitive protocol behavior? There seem to be endless pitches that go nowhere, I'm really surprised that 4 years in from Swift 2 and this still hasn't been a priority of the Core Team.

1 Like

If you want to have protocol methods dynamically dispatched, make sure that they are even a part of that protocol. For example:

protocol Foo {
    func a()
    func b()
}
extension Foo {
    func b() {}
    func c() {}
}

a() and b() are dynamically dispatched, but c() is not.

If you have a method that is defined in the protocol, but it isn't dynamically dispatched, please post a sample code, because that sounds like a bug.

all protocols use dynamic dispatch, as far as I know

I can't add it to the declaration, because of objc interop.

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

One thing I really, really, really badly want in Swift is the ability to add a method in a protocol extension (so a default implementation will exist), but still allow particular types to customise their implementation.

For example, let's say I wanted to make a wrapper that caches a Collection's indexes and provides RandomAccessCollection conformance. Types which already conform to RAC should be able to return themselves.

extension Collection {
  func makeRandomAccess() -> AnyRandomAccessCollection<Element> {
    let wrapper = CachedIndexCollection(self)
    return AnyRandomAccessCollection(wrapper)
  }
}

extension RandomAccessCollection {
  func makeRandomAccess() -> AnyRandomAccessCollection<Element> {
    return AnyRandomAccessCollection(self)
  }
}

func expensiveOperation<C: Collection>(_ collection: C) {
  // Will call the first version, every time.
  // The compiler doesn't know that it should dynamically-dispatch this.
  let rac = collection.makeRandomAccess()
  // ...
}

If I added a new protocol (which I do own, let's call it "CollectionExtensions"), and made this method a requirement, I would have to add separate conformances for every single Collection. We don't have the ability to say "all Collections conform to CollectionExtensions" and that feature is listed in the generics manifesto as highly unlikely.

How do you differentiate static dispatch from dynamic dispatch in protocols, given that protocols dispatch to methods based on the concrete type that conforms to the protocol?

And, what do you define to be "intuitive protocol behavior?" Objective-C protocol behavior?

Method calls through an existential are dynamically dispatched through the protocol witness table whenever the method being called is a requirement of the protocol. Methods declared in an extension of that protocol are always statically dispatched.

I can only guess what he means by intuitive protocol behavior, but my guess would be that default implementations of protocol requirements should be added to the vtable of conforming non-final classes, allowing the implementation to be overridden by subclasses.

1 Like

That sounds a lot like C++ virtual method dispatch in C++ classes/structs, which is not considered "dynamic" since function pointers are setup at compile/link time in the virtual method tables and not at runtime. Once I compile and link a concrete type derived from a protocol, the mental model is that function pointers to those protocol method requirements are placed into the jump tables pointed to by the protocol, and, at runtime, a jump is made. Extensions can extend protocol jump tables by either putting in default methods for defined requirements, or adding new methods that can be overridden. However, it all feels like "static dispatch" or call through a function pointer established during compile/link.

I would consider a "dynamic" dispatch mechanism something where you alter the dispatch mechanism at runtime, for example, monkey with the witness tables at run time ("swizzling"). Kind of like what Objective-C does with method selectors.

Oh well, guess need to get the terminology straight!

I think this is the same problem I've been running into, which has to do with generics and protocols.

Specifically, if you have a class based on a generic (e.g., "class Foo { var whatever: X }") then any references inside the class to "whatever" will be of X, no matter what else it may be when you instantiate the class.

class Base {
    func doit() {
	print("In base")
    }
}
class SubClass: Base {
    override func doit() {
	print("In subclass")
    }
}

func doSomething(_ object: Base) {
    print("Base class")
}
func doSomething(_ object: SubClass) {
    print("Derived class")
}

class Foo<X: Base> {
    var object: X
    init(_ object: X) {
	self.object = object
    }
    func doIt() {
	doSomething(self.object)
    }
}

let x = Foo(Base())
let y = Foo(SubClass())
x.doIt()
y.doIt()

which seems extremely counter-intuitive to me.