Jimmy Hsu Jimmy Hsu - 18 days ago 8
Swift Question

What is the compiler doing when I set an object's delegate to "self" in Swift?

In my code I have

UIViewController
that implements
UITextFieldDelegate
, and
UIPickerViewDelegate
.

The delegates for the
textField
and
pickerView
are assigned via
textField.delegate = self
and
pickerView.delegate = self
respectively.

I understand the protocols state for both only accept types that match their delegate, but I'm not sure how the compiler discerns between the two types of
UIPickerViewDelegate
and
UITextFieldDelegate
as the class is both protocols could be passed through as self.

What exactly is the compiler doing when I set
textField.delegate
and
pickerView.delegate
to
self
? It seems the compiler is smart enough to figure it out, but I'm curious to know what happens behind the scenes.

Ex:

class TextFieldViewController: UIViewController, UITextFieldDelegate, UIPickerViewDelegate {

@IBOutlet weak var textField: UITextField!
@IBOutlet weak var pickerView: UIPickerView!

override func viewDidLoad() {
super.viewDidLoad()

pickerView.delegate = self
textField.delegate = self
}

}

Answer

What exactly is the compiler doing when I set textField.delegate

There are two answers to this question which very much differ: one for pure Swift types and one for Objective-C.

As your example is about Objective-C (because UITextFieldDelegate is an objc protocol) I'm going to answer this one first.

Objective-C

The compiler's job is not only to produce code but also to check for correctness. It does so by making sure that the static type of the object assigned to a UITextField's delegate property matches the declaration. In this case the compiler would complain if self's type would not conform to UITextFieldDelegate.

This is called static type safety and is a pure compile time concept which does not affect code or runtime.

In a later stage the compiler has to produce code that performs the actual assignment. In case of setting an objc property it will look up the setter name (coming up with setDelegate:) and emit an objc_msgSend call with this selector.

The magic in this function is what makes it possible to send arbitrary messages to any object. Because this happens during runtime (the term is "late binding", or "dynamic dispatch") the compiler's job is done at this point.

When the text field tries to callback to the delegate, it uses the same mechanism to send messages. It does not have to know anything about the type of the delegate, apart from that it's an Objective-C object.

This is true for both Objective-C and Swift code, when using @objc instances.

Pure Swift

Everything changes when the delegate is a pure Swift protocol (not derived from the NSObject protocol). As your question is not specifically about this case I'm not going into too much detail.

In pure Swift there's no late binding. The connection between a name of a method and its implementation is made during compile time (while in Objective-C this happens during runtime).

As the delegate property in a hypothetical pure Swift TextField has to have a way to find protocol methods in the delegate, the compiler creates a so called witness table for the protocol and static type and stores that along with the instance. When calling a method in the delegate the compiler routes the call through the witness table to find the actual implementation. Much more work is done during compile time than in the Objective-C case.

If you want to know more about how Swift represents data and dispatches methods there's a 2016 WWDC video which goes into some detail.

Also, I very much recommend diving into the Swift ABI documentation. The section about Existential Container Layout describes this case.