Pull to refresh

Использование дженериков в Swift

Reading time16 min
Views18K
Original author: Matt Galloway
Если Вы уже разобрались, как программировать на 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, метод, который обрабатывает прикосновение с учетом пользовательского представления, выглядит следующим образом:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; 


Набор в этом методе, как известно, содержит только экземпляры UITouch, но только потому, что так сказано в документации. Но ничто не мешает объектам быть чем-то еще, и Вы обычно должны вычислить прикосновение в наборе как экземпляры UITouch, чтобы эффективно рассмотреть их как объекты UITouch.

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

func touchesBegan(touches: [UITouch]!, withEvent event: UIEvent!)


Это свидетельствует о том, что массив touches содержит только UITouch экземпляры, и компилятор выдаст ошибку, если код, обращаясь к этому методу, попытается передать что-либо еще. Мало того, что типы устанавливает/определяет компилятор, то Вам больше не нужно вычислять элементы экземпляров UITouch!

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

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

Как работают Дженерики

Чтобы проверить дженерики, Вы должны создать приложение, которое ищет изображения в Flickr.

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

Скомпилируйте и запустите приложение, и Вы увидите это:

image

Не очень все же! Не бойтесь, картинки с кошечьками скоро появятся.

Упорядоченные словари

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

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

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

Во многих языках программирования и фреймворках (включая Swift) наборы и словари не гарантируют какой-нибудь порядок, в отличие от массивов. Упорядоченный словарь выглядит как обычный словарь, но содержит ключи в определенном порядке. Вы будете использовать эту функциональность для хранения результатов поиска, что позволяет быстро найти результаты, а также поддерживать в порядке таблицу.

Если Вы были неосмотрительными, можно создать структуру пользовательских данных, чтобы обработать упорядоченный словарь. Но Вы дальновидны! Вы хотите создать что-то, что Вы можете использовать в приложениях в течение многих последующих лет! Дженерики — это идеальный вариант.

Структура первичных данных

Добавьте новый файл, выбрав File\New\File… и далее iOS\Source\Swift File. Нажмите на Next и назовите файл OrderedDictionary. Наконец, нажмите на Create.

В результате чего у вас будет пустой файл Swift и нужно будет добавить следующий код:

struct OrderedDictionary {  
}


Пока в этом нет ничего удивительного. Объект станет структурой, потому что он должен иметь семантику значений.

Примечание: Одним словом, семантика значений — это необычный способ сказать “скопировать/вставить”, а не “публичная ссылка”. Семантика значений дает много преимуществ, например, нет необходимости беспокоиться о другой части кода, который может неожиданно изменить ваши данные. Чтобы узнать больше, перейдите на Главу 3 как понять Swift с помощью уроков, “Классы и Структуры”.

Теперь Вы должны сделать его дженериком, таким образом, он сможет содержать любой тип значений, которые Вы хотите. Измените определение структуры на следующее:

struct OrderedDictionary<KeyType, ValueType>


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

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

Добавьте следующий код в определение структуры:

typealias ArrayType = [KeyType]
typealias DictionaryType = [KeyType: ValueType]
 
var array = ArrayType()
var dictionary = DictionaryType()


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

Обратите внимание, Вы можете использовать параметры типов KeyType и ValueType из определения структуры вместо типов. Массив представляет собой массив KeyTypes. Конечно, нет такой типа, как KeyType; вместо этого Swift рассматривает его как любой тип пользователя Упорядоченного словаря во время инстанцирования обобщенного типа.

В этот момент, Вы заметите ошибку компилятора:

Type 'Keytype' oes not conform to protocol 'Hashable'

Это могло бы быть сюрпризом для вас. Взгляните на реализацию Dictionary:

struct Dictionary<KeyType: Hashable, ValueType>


Это очень похоже на определения OrderedDictionary, за исключением одной вещи — “: Hashable” после KeyType. Hashable после точки с запятой показывает, что тип, переданный для KeyType, должен соответствовать протоколу Hashable. Это вызвано тем, что Dictionary должен уметь хешировать ключи для своей реализации.

Ограничить обобщенные параметры типа таким образом стало очень распространенно. Например, Вы могли бы ограничить тип значения, чтобы соответствовать протоколам Equatable или Printable в зависимости от того, что Ваше приложение должно сделать с теми значениями.

Откройте OrderedDictionary.swift и замените ваше определение структуры следующим:

struct OrderedDictionary<KeyType: Hashable, ValueType>


Это показывает, что KeyType для OrderedDictionary должен соответствовать Hashable. Это означает, что независимо от того каким типом становится KeyType, он все же будет приемлемым в качестве ключа для основного словаря.

Теперь файл будет компилироваться без ошибок!

Ключи, значения и все такое прочее

Какая польза от словаря, если Вы не можете добавить значения к нему? Откройте OrderedDictionary.swift и добавьте следующую функцию в ваше определение структуры:

// 1
mutating func insert(value: ValueType, forKey key: KeyType, atIndex index: Int) -> ValueType?
{
  var adjustedIndex = index
 
  // 2
  let existingValue = self.dictionary[key]
  if existingValue != nil {
    // 3
    let existingIndex = find(self.array, key)!
 
    // 4
    if existingIndex < index {
      adjustedIndex--
    }
    self.array.removeAtIndex(existingIndex)
  }
 
  // 5
  self.array.insert(key, atIndex:adjustedIndex)
  self.dictionary[key] = value
 
  // 6
  return existingValue
}


Все это ознакомит Вас с более новыми сведениями. Давайте рассмотрим их шаг за шагом:

  1. Метод, который помогает вставлять новый объект, insert(_:forKey:atIndex), должен иметь три параметра: value для конкретного key и index, по которому можно вставить пару ключ-значение. Здесь существует ключевое слово, которое вы возможно, не видели раньше: mutating (модифицирование).
  2. Вы направляете ключ к индексатору Dictionary, который возвращает существующее значение, если оно уже существует для того ключа. Этот метод insert имитирует то же поведение, как и updateValue Словаря и поэтому сохраняет существующее значение для ключа.
  3. Если есть существующее значение, то только тогда с помощью метода и находят индекс в массиве для того ключа.
  4. Если существующий ключ находиться перед индексом вставки, то Вы должны настроить индекс вставки, так как вам нужно будет удалить существующий ключ.
  5. При необходимости нужно будет обновлять массивы и словари.
  6. Наконец, Вы возвращаете существующее значение. Поскольку не могло бы быть существующего значения, так как функция возвращает дополнительное значение.


Теперь, когда у Вас есть возможность добавлять значения в словарь, что относительно того, чтобы удалить значения?

Добавьте следующую функцию к определению структуры в OrderedDictionary:

// 1
mutating func removeAtIndex(index: Int) -> (KeyType, ValueType)
{
  // 2
  precondition(index < self.array.count, "Index out-of-bounds")
 
  // 3
  let key = self.array.removeAtIndex(index)
 
  // 4
  let value = self.dictionary.removeValueForKey(key)!
 
  // 5
  return (key, value)
}


Давайте рассмотрим еще раз код шаг за шагом:

  1. Это — функция, которая видоизменяет внутреннее состояние структуры, и поэтому вы воспринимаете его как таковой. Название removeAtIndex соответствует методу на Array. Это — хорошая идея, рассматривать зеркальное копирование API в системной библиотеке, когда это нужно. Это помогает разработчикам, использующих Ваше API чувствовать себя свободно на платформе.
  2. Во-первых, Вы можете проверять индекс, чтобы увидеть, находиться ли он в пределах массива. Попытка удалить элемент за пределы допустимого диапазона от базового массива выдаст ошибку периода выполнения, таким образом, проверка обнаружит все это немного ранее. Возможно, вы использовали утверждения в Objective-C с функцией утверждать; assert доступно в Swift также, но precodition используется в данный момент в сборках конечных версий так, что ваши приложения могут завершиться, если предпосылки перестанут работать.
  3. Затем, Вы получите ключ из массива для заданного индекса, одновременно удаляя значение из массива.
  4. Затем Вы удалите значение для того ключа из словаря, который также возвращает значение, которое присутствовало прежде. Так как словарь может не содержать значение для данного ключа, removeValueForKey возвратит дополнительный материал. В этом случае Вы знаете, что словарь будет содержать значение для данного ключа, потому что, это единственный метод, который может добавить к словарю, является Вашей собственной insert(_:_:forKey:atIndex:), которую Вы написали. Таким образом, вы можете сразу же раскрыть дополнительный материал, зная, что там будет находиться значение.
  5. Наконец, Вы возвращаете ключ и значение в кортеж. Это соответствует поведению Массива removeAtIndex и Словаря removeValueForKey, которые возвращают существующие значения.


Доступ к значениям

Вы можете теперь записывать в словарь, но Вы не можете читать из него — это бесполезно для структуры данных! Теперь Вам нужно добавить методы, которые позволят Вам получать значения из словаря.

Откройте OrderedDictionary.swift и добавьте следующий код к структуре определения, и укажите под array и объявлениями переменной dictionary:

var count: Int {
  return self.array.count
}


Это — вычисляемое свойство для количества упорядоченного словаря, обычно необходимых данных для такой структуры данных. Количество в массиве будет всегда соответствовать количеству упорядоченного словаря, таким образом, все будет просто

Затем, Вам нужно получить доступ к элементам словаря. В Swift, Вы получите доступ к словарю, используя синтаксис индекса, следующим образом:

let dictionary = [1: "one", 2: "two"]
let one = dictionary[1] // Subscript


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

Добавьте следующий код в нижнюю часть определения структуры:

// 1
subscript(key: KeyType) -> ValueType? {
  // 2(a)
  get {
    // 3
    return self.dictionary[key]
  }
  // 2(b)
  set {
    // 4
    if let index = find(self.array, key) {
    } else {
      self.array.append(key)
    }
 
    // 5
    self.dictionary[key] = newValue
  }


Вот, что делает этот код:

  • Это работает так же, как и добавить поведение индекса, только вместо func или var, используется ключевое слово index. Параметр, в этом случае ключ, определяет объект, который появляется в квадратных скобках.
  • Нижние индексы могут содержать методы доступа, точно так же, как и вычисленные свойства. Заметьте, что (a) get и (b) set методы соответственно определяют методы доступа.
  • Метод get прост: Нужно запросить у словаря значения для данного ключа. Нижний индекс словаря уже возвращает дополнительный материал, что позволяет указать, что не существует никакое значение для того ключа.
  • Метод set является более сложным. Во-первых, он проверяет, существует ли ключ уже в упорядоченном словаре. Если он не существует, то Вы должны добавить его к массиву. Это целесообразно для нового ключа отправиться в конец массива, таким образом, Вы добавляете, значение в массив с помощью добавления.
  • И, наконец, Вы добавляете новое значение в словарь для данного ключа, переходя в новое значение через неявно именованную переменную newValue.


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

Классы и структуры могут иметь несколько индексных определений для различных типов аргументов. Добавьте следующую функцию в нижнюю часть определения структуры:

subscript(index: Int) -> (KeyType, ValueType) {
  // 1
  get {
    // 2
    precondition(index < self.array.count, 
                 "Index out-of-bounds")
 
    // 3
    let key = self.array[index]
 
    // 4
    let value = self.dictionary[key]!
 
    // 5
    return (key, value)
  }
}


Это подобно нижнему индексу, который Вы добавили ранее, за исключением того, что тип параметра — теперь Int, потому что, это то, что вы используете для ссылки на индекс массива. На сей раз, однако, тип результата — кортеж ключа и значения, потому что, именно Ваш OrderedDictionary хранит заданный индекс.

Как работает этот код:

  1. Этот нижний индекс имеет только геттер метод. Вы можете реализовать сеттер метод для него также, сначала проверив индексы, которые находятся в диапазоне размера упорядоченного словаря.
  2. Индекс должен находиться в пределах массива, который определяет длину упорядоченного словаря. Используете предусловие, чтобы предупредить программистов, которые попытаются получить доступ за пределами упорядоченного словаря.
  3. Ключ можно найти путем получения его из массива.
  4. Значение можно найти путем получения его из словаря для данного ключа. Опять же обратите внимание на использование развёрнутого дополнительного материала, потому что как, вы знаете, словарь должен содержать значение для любого ключа, который находится в массиве.
  5. Наконец, Вы возвращаете кортеж, содержащий ключ и значение.


Задание: Реализуйте сеттер для этого индекса. Добавьте набор с последующим завершением, как и в предыдущем определении индекса.

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

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

Тестирование системы

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

Создайте новый Playground, нажав на File\New\File…, выбрав iOS\Source\Playground и нажмите Next. Назовите его ODPlayground и затем нажмите Create.

Скопируйте и вставьте OrderedDictionary.swift в новый playground. Вы должны сделать это, потому что, к сожалению, на момент написания этого урока площадка не может «видеть» код в Вашем модуле приложения.

Примечание: Существует обходное решение для этого, кроме как метода скопировать/вставить, который здесь осуществляется. Если Вы переместили код своего приложения в фреймворк, то ваш Playground может получить доступ к вашему коду, как указывает Корин Крич.

Теперь добавьте следующий код в playground:

var dict = OrderedDictionary<Int, String>()
dict.insert("dog", forKey: 1, atIndex: 0)
dict.insert("cat", forKey: 2, atIndex: 1)
println(dict.array.description 
        + " : " 
        + dict.dictionary.description)
 
var byIndex: (Int, String) = dict[0]
println(byIndex)
 
var byKey: String? = dict[2]
println(byKey)


На боковой панели (или через View\Assistant Editor\Show Assistant Editor) можно увидеть выходную переменную println():

image

В этом примере, словарь имеет ключ Int, так как компилятор будет рассматривать тип переменной, которая определит, какой индекс нужно использовать. Так как byIndex является (Int, String) tuple, компилятор знает, что нужно использовать индексную версию стиля массива нижнего индекса, которая соответствует ожидаемому типу возвращаемого значения.

Попытайтесь удалить определение типа данных из одной переменной byIndex или byKey. Вы увидите ошибку компилятора, что свидетельствует о том, что компилятор не знает какой нижний индекс нужно использовать.

Подсказка: Чтобы запустить выведения типа, компилятор требует, чтобы тип выражения был однозначен. Когда несколько методов существует с теми же типами аргументов, но с разными типами возвращаемых, то вызывающая функция должна быть конкретной. Добавление метода в Swift может внести в сборку критические изменения, так что будьте внимательны!

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

Теперь Вы можете читать и писать в свой упорядоченный словарь! Это поможет заботиться о вашей структуре данных. Теперь Вы можете начинать работать с приложением!

Добавление поиска изображения

Пора вернуться назад к приложению.

Откройте MasterViewController.swift добавьте следующее переменное определение, чуть ниже двух @IBOutlets:

var searches = OrderedDictionary<String, [Flickr.Photo]>()


Это должен быть упорядоченный словарь, который содержит результат поиска, которые пользователь получил от Flickr. Как Вы можете видеть, он отображает String, критерий поиска, массив Flickr.Photo, или фотографии, возвращенные из API Flickr. Обратите внимание, Вы направляете ключ и значение в угловые скобки так же, как и для обычного словаря. Они становятся параметрами типа KeyType и ValueType в этой реализации.

Вы можете задаться вопросом, почему тип Flickr.Photo имеет период. Это потому, что фотография является классом, который определяется внутри класса Flickr. Эта иерархия является довольно полезной функцией Swiftа, помогая содержать пространство имен, сохраняя имена классов короткими. Внутри класса Flickr, вы можете использовать одну только Photo, которая относятся к классу фото, потому что контекст сообщает компилятору, что это такое.

Затем, найдите метод источника данных табличного представления под названием tableView(_:numberOfRowsInSection:) и измените его на следующий код:

func tableView(tableView: UITableView, 
               numberOfRowsInSection section: Int) -> Int
{
  return self.searches.count
}


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

Затем, найдите метод источника данных табличного представления tableView (_:cellForRowAtIndexPath:) и измените его на:

func tableView(tableView: UITableView, 
               cellForRowAtIndexPath indexPath: NSIndexPath)
              -> UITableViewCell
{
  // 1
  let cell = 
    tableView.dequeueReusableCellWithIdentifier("Cell", 
      forIndexPath: indexPath) as UITableViewCell
 
  // 2
  let (term, photos) = self.searches[indexPath.row]
 
  // 3
  if let textLabel = cell.textLabel {
    textLabel.text = "\(term) (\(photos.count))"
  }
  return cell
}


Вот что вы делаете в этом методе:

  1. Во-первых, выбираете ячейку из очереди из UITableView и направить ее непосредственно к UITableViewCell так как dequeueReusableCellWithIdentifier все еще возвращается к AnyObject (идентификатор в Objective-C) и не к UITableViewCell. Возможно, в будущем, Apple перепишет свою API, чтобы также можно было воспользоваться преимуществом дженериков!
  2. Затем, Вы получете ключ и значение для данной строки, используя нижний индекс по индексу, что вы написали.
  3. Наконец, Вы установите тектс в лейбу ячейки и вернете ячейку.


А теперь поговорим о «начинке». Найдите расширение UISearchBarDelegate и измените единственный метод следующим образом:

func searchBarSearchButtonClicked(searchBar: UISearchBar!) {
  // 1
  searchBar.resignFirstResponder()
 
  // 2
  let searchTerm = searchBar.text
  Flickr.search(searchTerm) {
    switch ($0) {
    case .Error:
      // 3
      break
    case .Results(let results):
      // 4
      self.searches.insert(results, 
                           forKey: searchTerm, 
                           atIndex: 0)
 
      // 5
      self.tableView.reloadData()
    }
  }
}


Этот метод вызывается тогда, когда пользователь нажимает на кнопку Поиск. Вот то, что делается в этом методе:

  1. Затем, обратитесь к критерию поиска в качестве текста в строке поиска прямо сейчас, и используете класс Flickr для поиска по этому запросу. Метод поиска Flickr использует как поисковый запрос так и замыкание для выполнения успешного или неуспешного поиска. Замыкание прибегает только к одному параметру: перечисление либо ошибок, либо результатов.
  2. В случае Ошибки, вы не увидите никаких знаков об этом. Но вы можете сделать, так что будет появляться предупреждение, ели вы этого захотите, но зачем все усложнять. Коду нужно остановиться, чтобы оповестить компилятор Swiftа, что ошибка не нанесет никакого вреда.
  3. Если поиск работает так как нужно, поиск возвращает результаты как сопоставленное значение в типе перечисления SearchResults. Вы можете добавлять результаты в верхнюю часть упорядоченного словаря, с условием поиска в качестве ключа. Если критерий поиска уже существует в словаре, то это перенесет критерий поиска в верхнюю часть списка и обновит его результаты.
  4. Наконец, Вы сможете перезагрузить таблицу экрана, потому что теперь у вас есть новые данные.


Йе-еху-у! Теперь ваше приложение будет искать изображения!

Скомпилируйте и запустите приложение и сделайте несколько поисков. Вы увидите нечто вроде этого:

image

Теперь повторите один из поисков, которые находятся не вверху списка. И вы увидите, как он возвратиться в начало списка:

image

Нажали на один из поисков и заметите, что он не показывает фотографий. Пора это исправить!

Дайте мне фотографии!

Откройте MasterViewController.swift и найдите метод prepareForSegue. И замените его на:

override func prepareForSegue(segue: UIStoryboardSegue, 
                              sender: AnyObject?)
{
  if segue.identifier == "showDetail" {
    if let indexPath = self.tableView.indexPathForSelectedRow()
    {
      let (_, photos) = self.searches[indexPath.row]
      (segue.destinationViewController
         as DetailViewController).photos = photos
    }
  }
}


При этом используется тот же метод searches упорядоченному словарю, как и при создании ячеек. Он не использует ключ (поиск по ключевому слову), тем не менее, таким образом, вы укажите тем самим, подчеркнувши, что эта часть tuple не должна быть связана с локальной переменной.

Скомпилируйте и запустите приложение, сделайте поиск, а затем нажмите на него. Вы увидите нечто вроде этого:

image

Кошкииии! Разве Вам не хочется помурлыкать вместо них от такого удовольствия?

Что дальше?

Примите мои поздравление, Вы узнали много нового о дженериках! Кроме того, Вы узнали о других интересных вещах, как индексирование, структуры, предусловие, и многое другое.

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

Я надеюсь, что Вы сможете использовать всю силу дженериков у Ваших будущих приложениях, чтобы избежать дублирования кода и оптимизировать код для многократного использования. Если у Вас есть какие-либо вопросы или комментарии, пожалуйста, присоединяйтесь к обсуждению на форуме!
Tags:
Hubs:
-6
Comments3

Articles

Change theme settings