Dynamic/Runtime Dispatch in Swift, or "The Strange Way Structs Behave in One Man's Opinion"

Dynamic/runtime dispatch in Swift, or the strange way structs behave in one man's opinion

In your first example you're overriding the description property. This implementation is therefore added to OddString's vtable (as it's a class), and can be dynamically dispatched to just fine, regardless of what the instance is statically typed as.

In your second example, you don't have a class – so no vtables. However you are conforming to a protocol. Protocols allow for dynamic dispatch via protocol witness tables (see this great WWDC talk on them), however this only happens for implementations of protocol requirements.

localizedDescription isn't a protocol requirement of the Error protocol, it's merely defined in a protocol extension of Error when you import Foundation (this is documented in SE-0112). Therefore it cannot be dynamically dispatched. Instead, it will be statically dispatched – so the implementation called is dependant on the static type of the instance.

That's the behaviour you're seeing here – when your explosive instance is typed as TestError, your implementation of localizedDescription is called. When typed as Error, the implementation in the Error extension is called (which just does a bridge to NSError and gets its localizedDescription).

If you want to provide a localised description, then you should conform your error type to LocalizedError instead, which defines errorDescription as a protocol requirement – thus allowing for dynamically dispatch. See this Q&A for an example of how to go about this.

Override of protocol default implementation in a subsubclass doesn't participate in dynamic dispatch

Thee are several rules involved here.

Sometimes Static Dispatch is used (in this case we must look at the type of the var/let to find out the implementation that will be used).

Other times Dynamic Dispatch is used instead (this means the implementation of the object inside the variable is used).

Let's consider the general example

let foo: SomeType1 = SomeType2()
foo.f()

I'll use the following definitions

  • classic implementation of f() to indicate when f() is defined outside of a protocol extension (so inside a struct/class).

  • default implementation of f() to indicate when f() is defined inside a protocol extension.

Dynamic Dispatch

If SomeType1 is a struct/class with it's own "classic" implementation of f() then polymorphism is applied.

It means that if SomeType2 doesn't have a classic implementation of f() then SomeType1.f() is used. Otherwise SomeType2.f() wins.

Static Dispatch

If SomeType1 doesn't have a classic implementation of f() but has a default implementation, then polymorphism is turned off.

In this case default implementation of the type of the let/var wins.

a.

Let's look at your first example

let a: A = C()
a.f() // "AAAA"

In this A doesn't have it's own classic implementation (because it's not a struct/class) but has a default implementation. So polymorphism is turned off and A.f() is used.

b.

Same rule for your second example

let b: B = C()
b.f() // "AAAA"

B doesn't have classic implementation of f(), but has a default implementation of f(). So polymorphism is turned off and B.f() (from the protocol extension) is used.

c.

Finally the object of type C is inside a constant of type C.

var c:C
c.f() // "CCCC"

Here C has a classic implementation of f(). In this case the protocol implementation is ignored and C.f() is used.

More

Let's see another example

protocol Alpha { }
extension Alpha { func f() -> String { return "Alpha"} }
protocol Beta { }
extension Beta { func f() -> String { return "Beta"} }

class Foo: Alpha, Beta { }

let alpha: Alpha = Foo()
alpha.f() // "Alpha"

let beta: Beta = Foo()
beta.f() // "Beta"

As you can see, again, the type of the constant containing the value wins. And if you put the Foo object inside a Foo constant you get a compile error

let foo: Foo = Foo()
foo.f() //

error: ambiguous use of 'f()'
foo.f()
^
Swift 2.playground:2:23: note: found this candidate
extension Beta { func f() -> String { return "Beta"} }
^
Swift 2.playground:6:24: note: found this candidate
extension Alpha { func f() -> String { return "Alpha"} }

Does Swift guarantee the storage order of fields in classes and structs?

Yes, the order of the struct elements in memory is the order of
their declaration. The details can be found
in Type Layout
(emphasis added). Note however the use of "currently", so this
may change in a future version of Swift:

Fragile Struct and Tuple Layout

Structs and tuples currently share the same layout algorithm, noted as the "Universal" layout algorithm in the compiler implementation. The algorithm is as follows:

  • Start with a size of 0 and an alignment of 1.
  • Iterate through the
    fields, in element order for tuples, or in var declaration order for
    structs. For each field:

    • Update size by rounding up to the alignment
      of the field, that is, increasing it to the least value greater or
      equal to size and evenly divisible by the alignment of the field.
    • Assign the offset of the field to the current value of size.
    • Update
      size by adding the size of the field.
    • Update alignment to the max of
      alignment and the alignment of the field.
  • The final size and alignment
    are the size and alignment of the aggregate. The stride of the type is
    the final size rounded up to alignment.

The padding/alignment is different from C:

Note that this differs from C or LLVM's normal layout rules in that size and stride are distinct; whereas C layout requires that an embedded struct's size be padded out to its alignment and that nothing be laid out there, Swift layout allows an outer struct to lay out fields in the inner struct's tail padding, alignment permitting.

Only if a struct is imported from C then it is guaranteed to have
the same memory layout. Joe Groff from Apple writes at
[swift-users] Mapping C semantics to Swift

If you depend on a specific layout, you should define the struct in C and import it into Swift for now.

and later in that discussion:

You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.

Example:

struct A {
var a: UInt8 = 0
var b: UInt32 = 0
var c: UInt8 = 0
}

struct B {
var sa: A
var d: UInt8 = 0
}

// Swift 2:
print(sizeof(A), strideof(A)) // 9, 12
print(sizeof(B), strideof(B)) // 10, 12

// Swift 3:
print(MemoryLayout<A>.size, MemoryLayout<A>.stride) // 9, 12
print(MemoryLayout<B>.size, MemoryLayout<B>.stride) // 10, 12

Here var d: UInt8 is layed out in the tail padding of var sa: A.
If you define the same structures in C

struct  CA {
uint8_t a;
uint32_t b;
uint8_t c;
};

struct CB {
struct CA ca;
uint8_t d;
};

and import it to Swift then

// Swift 2:
print(sizeof(CA), strideof(CA)) // 9, 12
print(sizeof(CB), strideof(CB)) // 13, 16

// Swift 3:
print(MemoryLayout<CA>.size, MemoryLayout<CA>.stride) // 12, 12
print(MemoryLayout<CB>.size, MemoryLayout<CB>.stride) // 16, 16

because uint8_t d is layed out after the tail padding of struct CA sa.

As of Swift 3, both size and stride return the same value
(including the struct padding) for structures imported from C,
i.e. the same value as sizeof in C would return.

Here is a simple function which helps to demonstrate the above (Swift 3):

func showMemory<T>(_ ptr: UnsafePointer<T>) {
let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size)
print(data as NSData)
}

The structures defined in Swift:

var a = A(a: 0xaa, b: 0x, c: 0xcc)
showMemory(&a) // <aa000000 cc>

var b = B(sa: a, d: 0xdd)
showMemory(&b) // <aa000000 ccdd>

The structures imported from C:

var ca = CA(a: 0xaa, b: 0x, c: 0xcc)
showMemory(&ca) // <aa000000 cc000000>

var cb = CB(ca: ca, d: 0xdd)
showMemory(&cb) // <aa000000 cc000000 dd000000>

Whats the difference between Enum, Structs, Classes

I think Chris Upjohn gives a rather simple yet great explanation on the topic in the Treehouse:

Enums

An enum is considered as a structured data type that can be modified without needing to change say a String or Int multiple times within your code, for example, the below shows how easy it would be to change something by accident and forget to change it somewhere else.

let myString = "test"

if myString == "ttest" {
// Doesn't execute because "ttest" is the value assigned to "myString"
}

With an enum we can avoid this and never have to worry about changing the same thing more than once.

enum MyEnum: String {
case Test = "test"
}

let enumValue = MyEnum.Test

if enumValue == MyEnum.Test {
// Will execute because we can reassign the value of "MyEnum.Test" unless we do so within "MyEnum"
}

Structs

I'm not sure how much you know about the MVC pattern but in Swift, this is a common practice, before I explain how structs are useful I'll give a quick overview of MVC in Swift.

Model - struct, useful for managing large amounts of data

View - Anything that extends UIView, more often than not this is a controller you manage on the storyboard

Controller - class, typically used only for views such as UIView controllers and UITableView

Moving on a struct as I said is used for managing large amounts of data, for instance, humans are a good example as we can use a struct to manage each person in a contact list.

struct Birthday {
var day: Int
var month: Int
var year: Double
}

struct Person {
var firstName: String
var lastName: String
var birthday: Birthday
var phoneNumber: String
var emailAddress: Int
}

For each contact you have you would create a new Person object that contains basic details along with a Birthday struct for complete reusability, the benefit to using the Birthday struct is the fact we can extend it without breaking our code, for example, if we needed an easy way to format the person's birthday we can add an additional function without affecting the rest of our code.

Classes

More often than not you would only find classes bound views, when bound to a view iOS will automatically assign a new instance of the class whenever a view is called, the second time the view is called it requests the already created instance of the class.

Other uses for a class is utility helpers which you can create as singletons, this is more of an advanced concept and generally you only need to create code like this on larger applications as generally everything you need is already built-in however it's recommend if you do need additional functionality that you use an extension which allows you to add to any built-in object and create your own subscripts.


Disclaimer: The following text belongs to Chris Upjohn. I could not have explained it any better in terms of Swift (maybe in general CS terms, using other languages), therefore I did not see any point rephrasing something similar to this.


Hope it helps!

Swift Protocol Extensions overriding

The short answer is that protocol extensions don't do class polymorphism. This makes a certain sense, because a protocol can be adopted by a struct or enum, and because we wouldn't want the mere adoption of a protocol to introduce dynamic dispatch where it isn't necessary.

Thus, in getColor(), the color instance variable (which may be more accurately written as self.color) doesn't mean what you think it does, because you are thinking class-polymorphically and the protocol is not. So this works:

let colorB = B().color // is "Red color" - OK

...because you are asking a class to resolve color, but this doesn't do what you expect:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

...because the getColor method is defined entirely in a protocol extension. You can fix the problem by redefining getColor in B:

class B: A, RedColor {
func getColor() -> String {
return self.color
}
}

Now the class's getColor is called, and it has a polymorphic idea of what self is.

Swift protocol extension method is called instead of method implemented in subclass

This is just how protocols currently dispatch methods.

A protocol witness table (see this WWDC talk for more info) is used in order to dynamically dispatch to implementations of protocol requirements upon being called on a protocol-typed instance. All it is, is really just a listing of the function implementations to call for each requirement of the protocol for a given conforming type.

Each type that states its conformance to a protocol gets its own protocol witness table. You'll note that I said "states its conformance", and not just "conforms to". BaseClass gets its own protocol witness table for conformance to MyProtocol. However SubClass does not get its own table for conformance to MyProtocol – instead, it simply relies on BaseClass's. If you moved the
: MyProtocol down to the definition of SubClass, it would get to have its own PWT.

So all we have to think about here is what the PWT for BaseClass looks like. Well, it doesn't provide an implementation for either of the protocol requirements methodA() or methodB() – so it relies on the implementations in the protocol extension. What this means is that the PWT for BaseClass conforming to MyProtocol just contains mappings to the extension methods.

So, when the extension methodB() method is called, and makes the call out to methodA(), it dynamically dispatches that call through the PWT (as it's being called on a protocol-typed instance; namely self). So when this happens with a SubClass instance, we're going through BaseClass's PWT. So we end up calling the extension implementation of methodA(), regardless of the fact that SubClass provides an implementation of it.

Now let's consider the PWT of JustClass. It provides an implementation of methodA(), therefore its PWT for conformance to MyProtocol has that implementation as the mapping for methodA(), as well as the extension implementation for methodB(). So when methodA() is dynamically dispatched via its PWT, we end up in its implementation.

As I say in this Q&A, this behaviour of subclasses not getting their own PWTs for protocols that their superclass(es) conform to is indeed somewhat surprising, and has been filed as a bug. The reasoning behind it, as Swift team member Jordan Rose says in the comments of the bug report, is

[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.

Therefore if this was the behaviour, already-compiled subclasses would lack any PWTs from superclass conformances that were added after the fact in another module, which would be problematic.


As others have already said, one solution in this case is to have BaseClass provide its own implementation of methodA(). This method will now be in BaseClass's PWT, rather than the extension method.

Although of course, because we're dealing with classes here, it won't just be BaseClass's implementation of the method that's listed – instead it will be a thunk that then dynamically dispatches through the class' vtable (the mechanism by which classes achieve polymorphism). Therefore for a SubClass instance, we'll wind up calling its override of methodA().



Related Topics



Leave a reply



Submit