есть ли какие либо ограничения на использование типов в шаблонных методах swift
Документация
Универсальные шаблоны
Универсальные шаблоны
Универсальный код позволяет вам писать гибкие, общего назначения функции и типы, которые могут работать с любыми другими типами, с учетом требований, которые вы определили. Вы можете написать код, который не повторяется и выражает свой контент в ясной абстрактной форме.
Проблема, которую решают универсальные шаблоны
Эта функция использует сквозные параметры для замещения значения a и b, что описано в Сквозные параметры.
Функция swapTwoInts(_:_:) обменивает начальные значения переменных a и b местами. Вы можете использовать эту функцию для замещения двух значений типа Int :
Было бы намного удобнее написать одну более гибкую функцию, которая бы могла заменить значения двух переменных любого типа. Универсальный код позволяет вам написать такую функцию. (Универсальная версия этой функции приведена ниже.)
Заметка
Универсальные функции
В двух примерах ниже T имеет значение типа Int и String соответственно:
Заметка
Параметры типа
В примере выше в функции swapTwoValues(_:_:) заполнитель имени типа T пример параметра типа. Параметры типа определяют и называют тип наполнителя, и пишутся сразу после имени функции, между угловыми скобками (например, ).
Как только вы определили параметр типа, то вы можете использовать его в качестве типа параметра функции (как например, параметры a и b в функции swapTwoValues(_:_:) ) или как возвращаемый функциональный тип, или как аннотация типа внутри тела функции. В каждом случае заполнитель типа отображается параметром типа, который заменяется на актуальное значение типа при вызове функции. (В swapTwoValues(_:_:) в нашем примере выше произошло замещение Т на Int в момент, когда функция была вызвана в первый раз, и на String при повторном вызове.)
Вы можете использовать несколько параметров типа, просто вписав их в угловых скобках через запятую.
Именование параметров типа
Заметка
Всегда давайте параметрам типа имена «горбатого» верхнего регистра (например, T и MyTypeParameter ), чтобы указать, что они являются заполнителем для типа, а не значением.
Универсальные типы
Заметка
Ниже приведена иллюстрация поведения добавления и удаления элемента из стека:
Вот как написать неуниверсальную версию стека, в этом случае мы используем стек для хранения Int значений:
Вот универсальная версия структуры:
Element определяет заполнитель имени типа для “какого-то типа Element ”, который будет предоставлен позже. Этот будущий тип может ссылаться на Element в любом месте определения структуры. В этом случае наш некоторый тип Element используется в трех местах:
Теперь stackOfStrings выглядит вот так после добавления последних четырех значений:
Удаляя последнее значение, он возвращает его и удаляет его из стека “ cuatro ”:
После удаления верхней величины, стек выглядит так:
Расширяем универсальный тип
Когда вы расширяете универсальный тип, вы не обеспечиваете список параметров в качестве определения расширения. Вместо этого, список параметров типа, из исходного определения типа, доступен внутри тела расширения, а имена исходных параметров типа используются для ссылки на параметры типа из исходного определения.
Вычисляемое свойство topItem теперь может быть использовано внутри экземпляра Stack для доступа к значению и для запроса к последнему элемента стека, без дальнейшего его удаления:
Расширения общего типа могут также включать требования, которые должны удовлетворять экземпляры расширенного типа, чтобы получить новые функциональные возможности, как описано в пункте Расширения с оговоркой where.
Ограничения типа
Функция swapTwoValues(_:_:) и тип Stack могут работать с любыми типами. Однако иногда бывает нужно внедрить определенные ограничения типа на типы, которые могут быть использованы вместе с универсальными функциями или универсальными типами. Ограничения типа указывают на то, что параметры типа должны наследовать от определенного класса или соответствовать определенному протоколу или композиции протоколов.
Синтаксис ограничения типа
Вы пишете ограничения типа, поместив ограничение единственного класса или протокола после имени параметра типа, и разделив их между собой запятыми, обозначая их в качестве части списка параметров. Базовый синтаксис для ограничений типа универсальной функции показан ниже (хотя синтаксис для универсальных типов такой же):
Ограничение типа в действии
Функция findIndex(ofString:in:) может быть использована для поиска строкового значения в массиве строк:
Связанные типы
Связанные типы в действии
Протокол Container определяет три требуемых возможности, которые должен иметь любой контейнер:
Любой тип, который удовлетворяет протоколу Equatable должен иметь возможность указывать на тип хранящихся элементов. Конкретно, он должен гарантировать, что только элементы правильного типа будут добавлены в контейнер, и должно быть ясно какой тип элементов будет возвращаться сабскриптом.
Для определения этих требований, протокол Container должен иметь способ ссылаться на тип элементов, которые он будет хранить, без указания типа элементов, которые может хранить конкретный контейнер. Протокол Container должен указать, что любое значение переданное в метод append(_:) должно иметь тот же тип, что и тип элементов контейнера, и что значение, возвращаемое сабскриптом контейнера, должно быть того же типа, что и элементы контейнера.
Тип IntStack реализует все три требования протокола Container, и в каждом случае оборачивает часть существующей функциональности типа IntStack для удовлетворения этих требований.
В этот раз тип параметра Element использован в качестве параметра item метода append(_:) и в качестве возвращаемого типа сабскрипта. Таким образом Swift может вывести, что Element подходящий тип для использования его в качестве типа Item для этого конкретного контейнера.
Расширение существующего типа для указания связанного типа
Вы можете расширить существующий тип для того, чтобы добавить соответствие протоколу, как описано в Добавление реализации протокола через расширение. Это включает в себя протокол со связанным типом.
Добавление ограничений в связанный тип
Использование протокола в ограничениях связанного типа
Ниже расширение типа Stack, которое добавляет соответствие протоколу SuffixableContainer :
Оговорка where
Ограничения типа, как описано в главе Ограничения типа, позволяют вам определять требования параметров типа связанных с универсальными функциями или типами.
Контейнеры не должны быть одного типа для того чтобы их проверить, хотя они и могут, но они должны содержать элементы одного и того же типа. Это требование выражается через комбинацию ограничений типа и оговоркой where :
Список типов параметров функции размещает следующие требования на два параметров типа:
Эти требования означают:
Эти требования позволяют функции allItemsMatch(_:_:) сравнивать два контейнера, даже если они являются контейнерами разного типа.
Вот как выглядит функция allItemsMatch(_:_:) в действии:
Расширения с оговоркой where
Вот как будет выглядеть наш метод isTop(_:) в действии:
Если вы попытаетесь вызвать метод isTop(_:) в стеке, то те, элементы, которые не реализуют протокол Equatable вызовут ошибку компиляции:
Контекстуальная оговорка Where
Вы можете написать универсальную оговорку where во время объявления, которая не будет иметь своих собственных универсальных ограничений по типу, когда вы уже работаете в контексте универсального типа. Например, вы можете написать универсальную оговорку where сабскрипту универсального типа или методу внутри расширения универсального типа. Структура Container является универсальной, и оговорка where в примере ниже определяет какого типа ограничения должны быть выполнены, чтобы эти новые методы были доступны контейнеру.
Связанные типы с универсальной оговоркой where
Универсальная оговорка where в Iterator требует, чтобы итератор должен поддерживать тот же самый тип элементов, что и тип элементов контейнера, не смотря на тип самого итератора. Метод makeIterator() предоставляет доступ к итератору контейнера.
Универсальные сабскрипты
Расширения протоколу Container добавляет сабскрипт, который принимает последовательность индексов и возвращает массив, содержащий элементы указанных индексов. Универсальный сабскрипт имеет следующие ограничения:
Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.
Сила дженериков в Swift. Часть 1
Всем привет! Делимся с вами переводом, подготовленным специально для студентов курса «iOS Разработчик. Продвинутый курс». Приятного прочтения.
Generic-функция, generic-тип и ограничения типа
Что такое дженерики?
Когда они работают – вы их любите, а когда нет – ненавидите!
Скажем, нам нужно создать массив, состоящий из значений целого типа и строк. Чтобы решить эту задачу, я создам две функции.
Теперь мне нужно вывести массив элементов типа float или массив пользовательских объектов. Если мы посмотрим на функции выше, то увидим, что используется только разница в типе. Поэтому вместо того, чтобы дублировать код, мы можем написать generic-функцию для повторного использования.
История дженериков в Swift
Generic-функции
Универсальные типы или параметрический полиморфизм
Универсальный тип Т из примера выше – это параметр типа. Можно указать несколько параметров типа, записав несколько имен параметров типа в угловые скобки, разделив их запятыми.
Примечание: Всегда давайте имена параметрам типа в нотации СamelCase (например, T и TypeParameter ), чтобы показать, что они являются названием для типа, а не значением.
Generic-типы
Это пользовательские классы, структуры и перечисления, которые могут работать с любым типом, аналогично массивам и словарям.
Давайте создадим стек
Сейчас этот стек способен принимать только целочисленные элементы, и если мне понадобится хранить элементы другого типа, то нужно будет либо создавать другой стек, либо преобразовывать этот к generic виду.
Ограничения Generic-типов
Поскольку дженерик может быть любого типа, многого с ним сделать не получится. Иногда полезно применять ограничения к типам, которые могут использоваться с generic-функциями или generic-типами. Ограничения типа указывают на то, что параметр типа должен соответствовать определенному протоколу или составу протокола.
Например, тип Dictionary в Swift накладывает ограничения на типы, которые могут использоваться в качестве ключей для словаря. Словарь требует, чтобы ключи были хэшируемыми для того, чтобы иметь возможность проверить, содержит ли он уже значения для определенного ключа.
Как работают дженерики? Посмотрим на пример.
Компилятору не хватает двух вещей, необходимых для создания кода функции:
Использование дженериков в Swift
Если Вы уже разобрались, как программировать на Swift, тогда Вы наверняка уже знаете основы языка Swift и как писать классы и структуры. Но Swift — больше, чем это — намного больше. Задача этой статьи расскажать о очень сильной стороне языка Swift, которая уже успела стать популярной в нескольких других языках под названием дженерики.
С помощью языка программирования безопасного по отношению к типам, это обычная проблема как написание кода, который действует только на один тип, но вполне корректен и для другого типа. Представьте себе, например, что функция добавляет два целых числа. Функция, которая добавляет два числа с плавающей запятой, выглядела бы очень похожей — но фактически, она будет выглядеть идентично.
Единственным отличием будет тип значения переменных.
В языке со строгим контролем типов Вы должны были бы определить отдельные функции как addInts, addFloats, addDoubles, и т.д., где у каждой функции был правильный аргумент, и типы возвращаемых значений.
Много языков программирования осуществляют решения этой проблемы. C++, например, использует шаблоны. Swift, как Java и C # используют дженерики — отсюда и тема этого урока!
В этой статье об обобщённом программировании Swift, Вы нырнете в мир существующих дженериков на языке программирования, в том числе и тех которых Вы уже видели. Тогда, создавайте программу для поиска фотографий в Flickr с пользовательской универсальной структурой данных для отслеживания критерия поиска пользователя.
Примечание: Эта статья о функциональном программировании Swift предназначен для тех, кто уже знает основы Swift. Если Вы плохо знакомы с основами языка Swift, мы рекомендуем Вам сначала посмотреть некоторые наши другие уроки о языке Swift.
Введение в дженерики
Вы не могли бы знать это, но Вы, вероятно, уже видели Дженерики в работе в Swift. Массивы и словари — классические примеры безопасности дженериков в деле.
Разработчики Objective-C привыкли к массивам и словарям, содержащие объекты разных типов в той же самой коллекции. Это обеспечивает большую гибкость, но знаете ли вы, что массив возвращенный из API предназначен для хранения? Вы можете убедиться, посмотрев на документацию или на имена переменных, другая форма документации. Даже в случае с документацией, не существует никаких способов (кроме кода без ошибок!), чтобы предотвратить сбои в коллекции во время выполнения.
С другой стороны Swift, имеет массивы и словари. Массив Ints может содержать только Ints и никогда не может (например) содержат String. Это означает, что Вы можете зарегистрировать код путем написания кода, что позволяет компилятору произвести проверку типов для вас.
Например, в Objective-C UIKit, метод, который обрабатывает прикосновение с учетом пользовательского представления, выглядит следующим образом:
Набор в этом методе, как известно, содержит только экземпляры UITouch, но только потому, что так сказано в документации. Но ничто не мешает объектам быть чем-то еще, и Вы обычно должны вычислить прикосновение в наборе как экземпляры UITouch, чтобы эффективно рассмотреть их как объекты UITouch.
В это время Swift не имеет набора, который определяется в стандартной библиотеке. Тем не менее, если вы использовали массив вместо набора, то вы могли бы написать вышеупомянутый метод следующим образом:
Это свидетельствует о том, что массив touches содержит только UITouch экземпляры, и компилятор выдаст ошибку, если код, обращаясь к этому методу, попытается передать что-либо еще. Мало того, что типы устанавливает/определяет компилятор, то Вам больше не нужно вычислять элементы экземпляров UITouch!
В целом, дженерики обеспечивают типы в качестве параметра класса. Все массивы действуют одинаково, храня значения в виде списка, но общие массивы параметризуют тип значения. Вы могли бы счесть это полезным, если это было так: Алгоритмы, которые вы будете использовать в массивах, не являются нетипоспецифическими, так что все массивы, со всеми типами значений, могут их разделить.
Теперь, когда у Вас есть основные сведения о дженериках и их пользе, Вы спокойно можете применить их к конкретному сценарию.
Как работают Дженерики
Чтобы проверить дженерики, Вы должны создать приложение, которое ищет изображения в Flickr.
Начните с загрузки стартового проекта для этого урока. Откройте его и быстро ознакомиться с основными классами. Класс Flickr может обрабатывать к API Flickr. Обратите внимание, ключ для API который находится в этом классе – предоставляется сразу, но Вы можете использовать свой собственный в случае, если вы хотите расширить приложение. Вы можете подписаться на один из них здесь.
Скомпилируйте и запустите приложение, и Вы увидите это:
Не очень все же! Не бойтесь, картинки с кошечьками скоро появятся.
Ваше приложение будет загружать изображения для каждого пользовательского запроса, и самые последний поиск будут отображать в виде списка в верхней части экрана.
Но что, если Ваш пользователь ищет тот же самый элемент дважды? Было бы хорошо, если бы приложение перенесло старые результаты в верхнюю часть нового списка и заменило его новым результатом.
Вы можете использовать массив для структуры данных чтобы вернуть результат, но в целях изучения дженериков, Вам нужно создать новую коллекцию: упорядоченный словарь.
Во многих языках программирования и фреймворках (включая Swift) наборы и словари не гарантируют какой-нибудь порядок, в отличие от массивов. Упорядоченный словарь выглядит как обычный словарь, но содержит ключи в определенном порядке. Вы будете использовать эту функциональность для хранения результатов поиска, что позволяет быстро найти результаты, а также поддерживать в порядке таблицу.
Если Вы были неосмотрительными, можно создать структуру пользовательских данных, чтобы обработать упорядоченный словарь. Но Вы дальновидны! Вы хотите создать что-то, что Вы можете использовать в приложениях в течение многих последующих лет! Дженерики — это идеальный вариант.
Структура первичных данных
Добавьте новый файл, выбрав File\New\File… и далее iOS\Source\Swift File. Нажмите на Next и назовите файл OrderedDictionary. Наконец, нажмите на Create.
В результате чего у вас будет пустой файл Swift и нужно будет добавить следующий код:
Пока в этом нет ничего удивительного. Объект станет структурой, потому что он должен иметь семантику значений.
Примечание: Одним словом, семантика значений — это необычный способ сказать “скопировать/вставить”, а не “публичная ссылка”. Семантика значений дает много преимуществ, например, нет необходимости беспокоиться о другой части кода, который может неожиданно изменить ваши данные. Чтобы узнать больше, перейдите на Главу 3 как понять Swift с помощью уроков, “Классы и Структуры”.
Теперь Вы должны сделать его дженериком, таким образом, он сможет содержать любой тип значений, которые Вы хотите. Измените определение структуры на следующее:
Элементы в угловых скобках являются параметрами типа дженерика. KeyType и ValueType не являются типами сами по себе, а становятся параметрами, которые можно использовать вместо типов в пределах определения структуры. Если вам не понятно, то все станет ясно в ближайшее время.
Самый простой способ реализовать упорядоченный словарь является поддержание, как массивов, так и словарей. Словарь будет хранить преобразование данных, а массив ключи.
Добавьте следующий код в определение структуры:
Это свидетельствует о двух свойствах, как описано, а также о двух псевдонимах типа, которые дают новое имя существующему типу. Здесь Вы соответственно даете псевдонимы массивам и типам словарей для резервных массивов и словарей. Псевдонимы типа — это отличный способ взять сложный тип и дать ему намного более короткое имя.
Обратите внимание, Вы можете использовать параметры типов KeyType и ValueType из определения структуры вместо типов. Массив представляет собой массив KeyTypes. Конечно, нет такой типа, как KeyType; вместо этого Swift рассматривает его как любой тип пользователя Упорядоченного словаря во время инстанцирования обобщенного типа.
В этот момент, Вы заметите ошибку компилятора:
Type ‘Keytype’ oes not conform to protocol ‘Hashable’
Это могло бы быть сюрпризом для вас. Взгляните на реализацию Dictionary:
Это очень похоже на определения OrderedDictionary, за исключением одной вещи — “: Hashable” после KeyType. Hashable после точки с запятой показывает, что тип, переданный для KeyType, должен соответствовать протоколу Hashable. Это вызвано тем, что Dictionary должен уметь хешировать ключи для своей реализации.
Ограничить обобщенные параметры типа таким образом стало очень распространенно. Например, Вы могли бы ограничить тип значения, чтобы соответствовать протоколам Equatable или Printable в зависимости от того, что Ваше приложение должно сделать с теми значениями.
Откройте OrderedDictionary.swift и замените ваше определение структуры следующим:
Это показывает, что KeyType для OrderedDictionary должен соответствовать Hashable. Это означает, что независимо от того каким типом становится KeyType, он все же будет приемлемым в качестве ключа для основного словаря.
Теперь файл будет компилироваться без ошибок!
Ключи, значения и все такое прочее
Какая польза от словаря, если Вы не можете добавить значения к нему? Откройте OrderedDictionary.swift и добавьте следующую функцию в ваше определение структуры:
Все это ознакомит Вас с более новыми сведениями. Давайте рассмотрим их шаг за шагом:
Теперь, когда у Вас есть возможность добавлять значения в словарь, что относительно того, чтобы удалить значения?
Добавьте следующую функцию к определению структуры в OrderedDictionary:
Давайте рассмотрим еще раз код шаг за шагом:
Доступ к значениям
Вы можете теперь записывать в словарь, но Вы не можете читать из него — это бесполезно для структуры данных! Теперь Вам нужно добавить методы, которые позволят Вам получать значения из словаря.
Откройте OrderedDictionary.swift и добавьте следующий код к структуре определения, и укажите под array и объявлениями переменной dictionary:
Это — вычисляемое свойство для количества упорядоченного словаря, обычно необходимых данных для такой структуры данных. Количество в массиве будет всегда соответствовать количеству упорядоченного словаря, таким образом, все будет просто
Затем, Вам нужно получить доступ к элементам словаря. В Swift, Вы получите доступ к словарю, используя синтаксис индекса, следующим образом:
Теперь Вы уже знакомы с синтаксисом, но вероятно только видели, что он использовался для массивов и словарей. Как же Вы бы использовали Ваши собственные классы и структуры? Swift, к счастью, позволяет легко добавить поведение индекса к пользовательским классам.
Добавьте следующий код в нижнюю часть определения структуры:
Вот, что делает этот код:
Теперь Вы можете индексировать в упорядоченный словарь, как будто это был обычный словарь. Вы можете получить значение для определенного ключа, но что относительно того, чтобы получить доступ с помощью индекса, как с массивом? Видя как это – работает с упорядоченным словарем, было бы неплохо также получить доступ к элементу через индекс.
Классы и структуры могут иметь несколько индексных определений для различных типов аргументов. Добавьте следующую функцию в нижнюю часть определения структуры:
Это подобно нижнему индексу, который Вы добавили ранее, за исключением того, что тип параметра — теперь Int, потому что, это то, что вы используете для ссылки на индекс массива. На сей раз, однако, тип результата — кортеж ключа и значения, потому что, именно Ваш OrderedDictionary хранит заданный индекс.
Как работает этот код:
Задание: Реализуйте сеттер для этого индекса. Добавьте набор с последующим завершением, как и в предыдущем определении индекса.
В этот момент, вы можете задаться вопросом, что произойдет, если KeyType является Int. Преимуществом дженериков является ввод любого хешового типа в качестве ключа, в том числе и Int. В этом случае, как же индекс знает, какой индекс код нужно использовать?
Вот где вам нужно будет дать больше информации о типе для компилятора, чтобы он знал, что ему делать. Обратите внимание, что каждый из индексов имеет другой тип возврата. Поэтому, если вы пытаетесь задать кортеж значения ключа, компилятор будет знать, что он должен использовать нижний индекс на подобии массива.
Давайте запустим программу так, чтобы Вы могли поэкспериментировать с тем, как выполнять компиляцию, какой метод индекса использовать, и как Ваш OrderedDictionary работает в целом.
Создайте новый Playground, нажав на File\New\File…, выбрав iOS\Source\Playground и нажмите Next. Назовите его ODPlayground и затем нажмите Create.
Скопируйте и вставьте OrderedDictionary.swift в новый playground. Вы должны сделать это, потому что, к сожалению, на момент написания этого урока площадка не может «видеть» код в Вашем модуле приложения.
Примечание: Существует обходное решение для этого, кроме как метода скопировать/вставить, который здесь осуществляется. Если Вы переместили код своего приложения в фреймворк, то ваш Playground может получить доступ к вашему коду, как указывает Корин Крич.
Теперь добавьте следующий код в playground:
На боковой панели (или через View\Assistant Editor\Show Assistant Editor) можно увидеть выходную переменную println():
В этом примере, словарь имеет ключ Int, так как компилятор будет рассматривать тип переменной, которая определит, какой индекс нужно использовать. Так как byIndex является (Int, String) tuple, компилятор знает, что нужно использовать индексную версию стиля массива нижнего индекса, которая соответствует ожидаемому типу возвращаемого значения.
Попытайтесь удалить определение типа данных из одной переменной byIndex или byKey. Вы увидите ошибку компилятора, что свидетельствует о том, что компилятор не знает какой нижний индекс нужно использовать.
Подсказка: Чтобы запустить выведения типа, компилятор требует, чтобы тип выражения был однозначен. Когда несколько методов существует с теми же типами аргументов, но с разными типами возвращаемых, то вызывающая функция должна быть конкретной. Добавление метода в Swift может внести в сборку критические изменения, так что будьте внимательны!
Поэкспериментируйте с упорядоченным словарем в playground, чтобы понять, как это работает. Попробуйте добавиться к нему, удалиться из него и изменить ключ и значение типов, прежде чем вернуться в приложение.
Теперь Вы можете читать и писать в свой упорядоченный словарь! Это поможет заботиться о вашей структуре данных. Теперь Вы можете начинать работать с приложением!
Добавление поиска изображения
Пора вернуться назад к приложению.
Откройте MasterViewController.swift добавьте следующее переменное определение, чуть ниже двух @IBOutlets:
Это должен быть упорядоченный словарь, который содержит результат поиска, которые пользователь получил от Flickr. Как Вы можете видеть, он отображает String, критерий поиска, массив Flickr.Photo, или фотографии, возвращенные из API Flickr. Обратите внимание, Вы направляете ключ и значение в угловые скобки так же, как и для обычного словаря. Они становятся параметрами типа KeyType и ValueType в этой реализации.
Вы можете задаться вопросом, почему тип Flickr.Photo имеет период. Это потому, что фотография является классом, который определяется внутри класса Flickr. Эта иерархия является довольно полезной функцией Swiftа, помогая содержать пространство имен, сохраняя имена классов короткими. Внутри класса Flickr, вы можете использовать одну только Photo, которая относятся к классу фото, потому что контекст сообщает компилятору, что это такое.
Затем, найдите метод источника данных табличного представления под названием tableView(_:numberOfRowsInSection:) и измените его на следующий код:
Этот метод теперь использует упорядоченный словарь, который указывает, сколько ячеек имеет наша таблица.
Затем, найдите метод источника данных табличного представления tableView (_:cellForRowAtIndexPath:) и измените его на:
Вот что вы делаете в этом методе:
А теперь поговорим о «начинке». Найдите расширение UISearchBarDelegate и измените единственный метод следующим образом:
Этот метод вызывается тогда, когда пользователь нажимает на кнопку Поиск. Вот то, что делается в этом методе:
Йе-еху-у! Теперь ваше приложение будет искать изображения!
Скомпилируйте и запустите приложение и сделайте несколько поисков. Вы увидите нечто вроде этого:
Теперь повторите один из поисков, которые находятся не вверху списка. И вы увидите, как он возвратиться в начало списка:
Нажали на один из поисков и заметите, что он не показывает фотографий. Пора это исправить!
Дайте мне фотографии!
Откройте MasterViewController.swift и найдите метод prepareForSegue. И замените его на:
При этом используется тот же метод searches упорядоченному словарю, как и при создании ячеек. Он не использует ключ (поиск по ключевому слову), тем не менее, таким образом, вы укажите тем самим, подчеркнувши, что эта часть tuple не должна быть связана с локальной переменной.
Скомпилируйте и запустите приложение, сделайте поиск, а затем нажмите на него. Вы увидите нечто вроде этого:
Кошкииии! Разве Вам не хочется помурлыкать вместо них от такого удовольствия?
Примите мои поздравление, Вы узнали много нового о дженериках! Кроме того, Вы узнали о других интересных вещах, как индексирование, структуры, предусловие, и многое другое.
Если Вы хотите узнать больше о Дженериках, вам нужно посмотреть полную главу учебного пособия о Swift.
Я надеюсь, что Вы сможете использовать всю силу дженериков у Ваших будущих приложениях, чтобы избежать дублирования кода и оптимизировать код для многократного использования. Если у Вас есть какие-либо вопросы или комментарии, пожалуйста, присоединяйтесь к обсуждению на форуме!