State of gnustep interop

This is a very broad topic because there are any number of ways that the Swift and ObjC standard libraries can interact with each other. Even sticking with core language features, I'm afraid I have to give a somewhat abbreviated explanation.

It's useful to think of class types as being organized into clusters that share a "class model" which gives general rules for how you can correctly implement basic class operations on them:

  • managing the ownership of an instance reference
  • the representation of types, if any
  • retrieving the dynamic type of an instance reference
  • performing upcasts and downcasts in the class hierarchy
  • testing reference equality
  • making a dynamically-dispatched method call
  • defining a class or subclass

Generally, class models must be consistent within a class hierarchy. Models tend to demand common resources, like storing a pointer to a particular structure at offset 0 in an object, and this makes them incompatible with each other. For example, polymorphic C++ classes store a v-table pointer at offset 0, while ObjC classes have an "is-a" (a heavily constrained pointer to an ObjC class object).

Like any type, class references can be handled totally generically, e.g. as an Any or in code that's generic over an unconstrained type T. Such code has no idea that it's working with a class reference at all, and so features that are specific to class references are generally disabled, or at least abstracted. For example, if you make a class type conform to a non-class-constrained protocol, a protocol requirement might be implemented with a non-final method; uses of that requirement will call a "thunk" function which does the necessary class method dispatch, so that the user doesn't need to know they're dealing with a class reference.

At the other end, when we have a concrete type like NSWindow, we can presumptively trust that type to tell us the right class model to use, and no interoperation is required.

A big source of complexity for Swift on Apple platforms is that we want Swift and ObjC classes to share at least some aspects of the class model: we want to have the type AnyObject in Swift and the type id in ObjC to be able to hold any Swift or ObjC class reference. Without this, we'd have a lot of artificial burdens when using the Apple platform APIs from Swift. So even totally native Swift classes have to have ObjC-compatible class objects and "is-a" references, and if you look at them through the ObjC runtime APIs, they always have an implicit ObjC superclass, get registered with the ObjC runtime, and so on. You can even do ObjC method dispatch on them, and it'll find methods that are (implicitly or explicitly) @objc. That's not a burden we want to force on platforms that don't have ObjC as a major part of the platform API.

3 Likes