Ответ
Имплементация по умолчанию — это реализация метода или свойства протокола, предоставляемая в его extension. Она используется, если соответствующий тип, подписанный на протокол, не предоставляет своей собственной реализации.
Когда происходит выбор имплементации? Выбор между реализацией по умолчанию и кастомной реализацией типа происходит на этапе компиляции (статическая диспетчеризация) для методов, объявленных в расширении протокола. Это не то же самое, что динамическая диспетчеризация через таблицу виртуальных функций (vtable) для методов класса.
Пример и важный нюанс:
protocol Renderable {
func draw()
}
extension Renderable {
// Имплементация по умолчанию
func draw() { print("Default drawing") }
}
struct Square: Renderable {
// У Square НЕТ своей реализации draw()
}
struct Circle: Renderable {
// У Circle ЕСТЬ своя реализация
func draw() { print("Drawing a circle") }
}
let shapes: [Renderable] = [Square(), Circle()]
for shape in shapes {
shape.draw() // Вызовется ТА реализация, которая известна на момент компиляции для каждого типа.
// Square.draw() -> Default drawing (из extension)
// Circle.draw() -> Drawing a circle (из структуры)
}
Ключевой вывод: Имплементация по умолчанию — это инструмент для уменьшения дублирования кода и предоставления базовой функциональности, которую тип может переопределить при необходимости.
Ответ 18+ 🔞
А, ну это же протоколы в свифте, да? Слушай, тут есть одна тема, которая мозги выносит новичкам, но на самом деле всё просто, как три копейки.
Вот смотри, есть у тебя протокол — типа контракта, договора: «эй, чувак, если хочешь со мной работать, у тебя должен быть вот такой метод draw()». И всё бы ничего, но писать каждому эту хуйню — заебёшься. Поэтому придумали имплементацию по умолчанию. Это как если бы ты сказал: «ладно, вот тебе готовая реализация в extension, бери и пользуйся, если сам не придумал ничего умнее».
А теперь главный подвох, на котором все обжигаются:
Выбор, какую именно реализацию вызывать — твою кастомную или дефолтную — происходит ещё на этапе компиляции. Это не магия рантайма, как с классами. Компилятор смотрит: «ага, Circle написал свой draw() — окей, берём его. Square нихуя не написал — ну тогда берём дефолтный из расширения протокола». И всё, приехали.
Вот, глянь на этот код, тут всё как на ладони:
protocol Renderable {
func draw()
}
extension Renderable {
// Это и есть та самая «затычка», если у типа своя голова не варит
func draw() { print("Default drawing") }
}
struct Square: Renderable {
// Тишина. Ничего своего. Значит, будет дефолтный draw.
}
struct Circle: Renderable {
// А вот этот умник решил по-своему
func draw() { print("Drawing a circle") }
}
let shapes: [Renderable] = [Square(), Circle()]
for shape in shapes {
shape.draw() // Компилятор уже всё решил за нас!
// Square -> пойдёт дефолтная печать "Default drawing"
// Circle -> а этот запорет своё "Drawing a circle"
}
Итог, блядь: Имплементация по умолчанию — это не волшебная палочка, а просто удобная хуйня, чтобы не копипастить один и тот же код в десять мест. Хочешь быть уникальным — переопределяй. Не хочешь — сиди на дефолте, как Square. Всё просто, ёпта. Главное — помни, что решение принимается статически, а не в рантайме, и тогда у тебя не будет неожиданных пиздецов в коде.