Программирование
Пакеты параметров типа и значения в Swift с объяснением
Пакеты параметров значения и типа позволяют сократить количество перегрузок и писать общие функции, принимающие произвольное количество аргументов разных типов.
Пакеты параметров типа и пакеты параметров значения позволяют написать общую функцию, принимающую произвольное количество аргументов разных типов. В результате выпуска SE-393, SE-398 и SE-399 вы сможете использовать эту новую возможность, начиная со Swift 5.9.
Одним из наиболее заметных мест, на которые повлияют эти изменения, будет ограничение в 10 View в SwiftUI, которое больше не существует благодаря вариативным дженерикам, ставшими возможными после этих предложений. Также вероятно, что основной код, который вы уже использовали, теперь можно переписать с использованием пакетов параметра. Это продвинутая функция в Swift, но давайте посмотрим, какие преимущества вы можете получить от ее использования в своих проектах.
Что такое пакеты параметров типа и значения?
Пакеты параметров типа (type parameter pack) и значений (value parameter pack) всегда используются вместе. Их длина совпадает, а соответствующий входной индекс равен выходному индексу. Без контекста это трудно понять, поэтому рассмотрим пример:
func eachFirst<each T: Collection>(_ item: repeat each T) -> (repeat (each T).Element?) { return (repeat (each item).first) }
В данном примере мы определили новый глобальный метод eachFirst
, позволяющий передавать произвольное количество массивов и получать в качестве возвращаемого значения такое же количество необязательных элементов.
Сначала мы определили пакет параметров типа each T: Collection
. Другими словами, у нас есть пакет параметров типа, состоящий из коллекций. То же самое относится и к аргументу функции repeat each T
, который сообщает нам, что мы будем повторять каждую входную коллекцию.
Функция возвращает пакет параметров значения. В данном случае это пакет значения, содержащий все первые элементы входных коллекций.
В качестве примера воспользуемся методом на примере коллекции целых чисел и имен:
let numbers = [0, 1, 2] let names = ["Antoine", "Maaike", "Sep"] let firstValues = eachFirst(numbers, names) print(firstValues) // Optional(0), Optional("Antoine")
Как я уже говорил, входной индекс совпадает с выходным. В качестве первого аргумента мы передаем массив чисел, а его первое значение возвращается как первый элемент в сформированном пакете значений.
Какие проблемы решают пакеты параметров
Пакеты параметров помогают нам писать многократно используемый код и избавляют нас от необходимости писать много перегрузок. До появления пакетов параметров нам пришлось бы писать множество перегрузок следующим образом:
func eachFirst<T>( _ item: T ) -> T? func eachFirst<T1, T2>( _ item1: T1, _ item2: T2 ) -> (T1?, T2?) func eachFirst<T1, T2, T3>( _ item1: T1, _ item2: T2, _ item3: T3 ) -> (T1?, T2?, T3?)
Вы можете узнать эти перегрузки в таких операторах Combine, как zip
, combineLatest
и merge
. Эти перегрузки также послужили причиной ограничения на 10 представлений в SwiftUI, поскольку параметр body представления использует метод базового блока построения @ViewBuilder
, который был определен следующим образом:
static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0: View, C1: View, C2: View, C3: View, C4: View, C5: View, C6: View, C7: View, C8: View, C9: View { return .init((c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)) }
Приведенный выше пример является завершающим методом перегрузки всех перегрузок блока сборки:
Начиная со Swift 5.9, этот метод переписывается с использованием пакетов параметров типа и значения:
static func buildBlock<each Content>(_ content: repeat each Content) -> TupleView<(repeat each Content)> where repeat each Content : View
Почему я не могу использовать массивы?
Одним из первых вопросов, который у меня возник, был вопрос о том, нельзя ли просто использовать массивы. Однако пример, которым я поделился ранее, был бы невозможен без стирания типов.
Чтобы продемонстрировать это, я переписал предыдущий пример, используя только дженерики:
func eachFirst<T: Collection>(collections: [T]) -> [T.Element?] { collections.map(\.first) }
Если мы передадим в качестве аргументов те же числа и имена, то столкнемся со следующей ошибкой:
Поскольку в качестве входных параметров мы используем массивы int и string, компилятор не может выдать результирующее значение того же типа.
Требование минимальной длины аргумента
Методы, которые вы пишете с использованием пакетов параметров типа и значения, скорее всего, требуют наличия хотя бы одного аргумента, чтобы быть полезными. Например, наш метод eachFirst
бесполезен, если мы не передаем никакого значения, и даже выдает предупреждение:
let firstValues = eachFirst() // Warning: Constant 'firstValues' inferred to have type '()', which may be unexpected
Поэтому рекомендуется переписывать методы с использованием ведущего аргумента, чтобы требовать передачи хотя бы одного аргумента:
func eachFirst<FirstT: Collection, each T: Collection>(_ firstItem: FirstT, _ item: repeat each T) -> (FirstT.Element?, repeat (each T).Element?) { return (firstItem.first, repeat (each item).first) }
В конечном результате мы можем передать один или несколько массивов:
let numbers = [0, 1, 2] let names = ["Antoine", "Maaike", "Sep"] let booleans = [true, false, true] let doubles = [3.3, 4.1, 5.6] let firstValues = eachFirst(numbers, names, booleans, doubles) print(firstValues) // (Optional(0), Optional("Antoine"), Optional(true), Optional(3.3))
Заключение
Пакеты параметров значения и типа позволяют сократить количество перегрузок и писать общие функции, принимающие произвольное количество аргументов разных типов. Хотя это продвинутая возможность Swift, вы можете обнаружить в своей кодовой базе похожие перегрузки, которые можно начать переписывать с помощью пакетов параметров.
Спасибо!
-
Интегрированные среды разработки2 недели назад
Лучшая работа с Android Studio: 5 советов
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2024.43
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2024.44
-
Исследования2 недели назад
Поможет ли новая архитектура React Native отобрать лидерство у Flutter в кроссплатформенной разработке?