Видеокурсы по изучению языка программирования Swift. Подробнее

Расширения протоколов

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Протоколы могут быть расширены для обеспечения метода и реализации свойства соответствующими типами. Это позволяет вам самостоятельно определить поведение по протоколам, а не по индивидуальному соответствию каждого типа или глобальной функции.

Например, RandomNumberGenerator протокол может быть расширен для обеспечения randomBool() метода, который использует результат вызванного random() метода для возврата random (случайного) значения Bool:

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

Создавая расширение по протоколу, все соответствующие типы автоматически получают эту реализацию метода без каких-либо дополнительных изменений.

let generator = LinearCongruentialGenerator()
print("Рандомное число: \(generator.random())")
// Выведет "Рандомное число: 0.37464991998171"
print("Рандомное логическое значение: \(generator.randomBool())")
// Выведет "Рандомное логическое значение: true"

Обеспечение реализации по умолчанию (дефолтной реализации)

Вы можете использовать расширение протокола, чтобы обеспечить реализацию по умолчанию для любого метода или требования свойства этого протокола. Если соответствующий тип предоставляет свою собственную реализацию требуемого метода или свойства, то реализация будет использоваться вместо той, которая предоставляется расширением.

Заметка

Требования протокола с реализацией по умолчанию, предоставляемой расширениями, отличаются от опциональных требований протокола. Хотя соответствующие типы не должны предоставлять свою собственную реализацию, требования с реализацией по умолчанию могут быть вызваны без опциональных последовательностей.

Например, протокол PrettyTextRepresentable, который наследует от протокола TextRepresentable может предоставлять дефолтную реализацию требуемого свойства prettyTextualDescription, просто возвращая результат обращения к свойству textualDescription:

extension PrettyTextRepresentable  {
    var prettyTextualDescription: String {
        return textualDescription
    }
}

Добавление ограничений к расширениям протоколов

Когда вы определяете расширение протокола, вы можете указать ограничения для принимающих типов, которые они должны удовлетворить до того, как будут доступны методы и свойства расширения. Вы записываете эти ограничения сразу после имени протокола, при помощи оговорки where.

Например, вы можете определить расширение протокола Collection, которое применимо ко всем коллекциям, чьи элементы соответствуют протоколу TextRepresentable из примера ранее.

extension Collection where Iterator.Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

Свойство textualDescription возвращает текстовое описание всей коллекции, соединяя текстовое отображение каждого элемента коллекции в список, разделенных между элементов, которые заключены в общие квадратные скобки.

Рассмотрим структуру Hamster (см. выше), которая соответствует протоколу TextRepresentable, и массив значений типа Hamster:

let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]

Так как Array соответствует протоколу Collection, а элементы массива протоколу TextRepresentable, то массив может использовать свойство textualDescription, чтобы получить описание своего содержимого:

print(hamsters.textualDescription)
// Выведет "[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]”

Заметка

Если подписанный тип удовлетворяет требованиям нескольких ограничивающих расширений, которые предоставляют реализации для одного и того же метода или свойства, то Swift будет использовать ограничения для самого строгих ограничений.

 

Swift: 
4.0