Статьи

iOS с нуля с помощью Swift: изучение Foundation Framework

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

В предыдущей статье я кратко упомянул Core Foundation и его связь с Foundation. Несмотря на то, что мы не будем явно использовать платформу Core Foundation в оставшейся части этой серии, полезно ознакомиться с этой платформой и узнать, чем она отличается от ее объектно-ориентированного элемента, Foundation, который вы будете широко использовать.

В то время как платформа Foundation реализована в Objective-C, платформа Core Foundation основана на языке C. Несмотря на это различие, платформа Core Foundation реализует ограниченную объектную модель. Эта объектная модель позволяет определять набор непрозрачных типов, которые часто называют объектами, несмотря на то, что они, строго говоря, не являются объектами.

Основная цель обеих платформ схожа, позволяя обмениваться данными и кодом между различными библиотеками и платформами. Базовый фонд также включает поддержку интернационализации. Ключевой компонент этой поддержки предоставляется через непрозрачный тип CFString , который эффективно управляет массивом символов Unicode .

Как я уже упоминал ранее, мост по бесплатному соединению буквально сокращает разрыв между обеими платформами, позволяя заменять объекты Какао на Базовые объекты Основы в параметрах функций и наоборот.

Важно отметить, что автоматический подсчет ссылок (ARC) не управляет «объектами» Core Foundation. Это означает, что вы несете ответственность за управление памятью при работе с Core Foundation. Майк Эш написал отличную статью об автоматическом подсчете ссылок и о том, как использовать ARC с Core Foundation и бесплатными мостами.

Посетите справочник по базовой платформе Apple Core Foundation, если вы хотите узнать больше о платформе и получить полный список непрозрачных типов платформы.

Изучение нового навыка лучше всего делать на практике. Давайте начнем с создания новой игровой площадки в Xcode. Назовите игровую площадку Foundation и установите Platform на iOS . Нажмите Далее, чтобы продолжить. Сообщите Xcode, где вы хотите сохранить игровую площадку, и нажмите « Создать» .

Создание новой игровой площадки

Вот как должно выглядеть содержимое детской площадки.

1
2
3
4
5
//: Playground — noun: a place where people can play
 
import UIKit
 
var str = «Hello, playground»

Детская площадка содержит комментарий вверху, оператор импорта для инфраструктуры UIKit и объявление переменной. Справа вы можете увидеть выход игровой площадки.

Поскольку мы будем работать с платформой Foundation, нам нужно заменить оператор импорта для UIKit на оператор импорта для Foundation. Удалите комментарий вверху, обновите оператор импорта и удалите объявление переменной.

1
import Foundation

Фреймворк Foundation — это нечто большее, чем набор классов для работы с числами, строками и коллекциями (массивами, словарями и наборами). Он также определяет десятки протоколов, функций, типов данных и констант.

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

Несколько языков, таких как Perl, Python и C ++, обеспечивают поддержку множественного наследования . Это означает, что класс может происходить из подкласса более чем одного класса.

Хотя Swift и Objective-C не предоставляют поддержку множественного наследования, они поддерживают множественное наследование посредством спецификации в форме протоколов. Мы уже рассматривали протоколы ранее в этой серии, но есть важная деталь, которую мы еще не обсуждали.

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

Поскольку подавляющее большинство платформ iOS SDK написано на Objective-C, мы встретимся с рядом протоколов Objective-C, которые определяют дополнительные методы. Важно помнить, что требуется каждое свойство и метод протокола Swift.

Преимущества протоколов многообразны. Когда класс принимает или соответствует протоколу, ожидается, что класс реализует методы, объявленные протоколом. Для некоторых из вас протоколы могут напоминать интерфейсы в Java. Это означает, что протокол может использоваться для объявления интерфейса с объектом без раскрытия типа объекта.

Многократное наследование имеет свои преимущества, но, безусловно, имеет свои недостатки . Преимущество протоколов заключается в том, что несвязанные типы, перечисления, классы и структуры могут по-прежнему иметь сходное поведение при использовании протоколов.

В дополнение к корневому классу NSObject платформа Foundation также определяет протокол NSObject . Однако в Swift классы и протоколы не могут иметь одинаковые имена. В результате протокол NSObject известен как NSObjectProtocol в Swift.

Объекты, соответствующие протоколу NSObjectProtocol , могут спрашивать об их классе и суперклассе, могут сравниваться с другими объектами и отвечать на self . Это лишь небольшая часть поведения, добавляемого к объектам, соответствующим протоколу NSObjectProtocol .

Объекты, соответствующие протоколу NSCoding , могут быть закодированы и декодированы. Это необходимо для объектов, которые необходимо архивировать или распространять. Архивирование объекта, например, происходит, когда объект или граф объекта сохраняются на диске.

Протокол NSCopying объявляет только один метод, copyWithZone(_:) . Если класс должен поддерживать копирование объектов, он должен соответствовать протоколу NSCopying . Копирование объекта осуществляется путем отправки ему сообщения copy() или copyWithZone(_:) .

Класс NSObject является корневым классом подавляющего большинства иерархий классов Objective-C. Почему это важно, если мы заинтересованы в Swift? Помните, что большинство платформ iOS SDK работают на Objective-C. Даже если вы решите разработать приложение на Swift, вы все равно будете использовать Objective-C под капотом.

Унаследовавшись от корневого класса NSObject , объекты знают, как вести себя как объекты Objective C и как взаимодействовать со средой выполнения Objective C. Не должно быть сюрпризом, что NSObject соответствует протоколу NSObject/NSObjectProtocol , который мы обсуждали несколько минут назад.

Давайте посмотрим, что мы получим бесплатно, если класс наследуется от класса NSObject . Добавьте следующий фрагмент кода на свою игровую площадку. Это должно выглядеть знакомо, если вы читали предыдущие статьи этой серии. Мы объявляем класс Book , который наследуется от NSObject . Класс Book реализует два метода экземпляра, chapters() и pages() .

1
2
3
4
5
6
7
8
9
class Book: NSObject {
    func chapters() {
         
    }
     
    func pages() {
         
    }
}

Поскольку Book наследуется от NSObject а NSObject соответствует протоколу NSObjectProtocol , мы можем спросить класс Book о его поведении и дереве наследования. Посмотрите на следующий пример.

Осматривая Книжный Класс

Сначала мы спрашиваем класс Book о себе, вызывая classForCoder() для класса Book . Метод classForCoder() является методом класса, что означает, что мы можем вызвать этот метод для самого класса Book . Затем мы спрашиваем класс Book о его суперклассе или родительском классе, классе, от которого он напрямую наследует. Это возвращает необязательный, так как не каждый класс имеет родительский класс. Неудивительно, что суперклассом Book является NSObject .

Следующая строка говорит нам, что Book соответствует протоколу NSObjectProtocol , что не является неожиданностью, поскольку оно наследует эту черту от класса NSObject . Мы также узнаем, что Book не соответствует протоколу NSCoding .

Похоже, что класс Book не отвечает на метод chapters() . Это не так уж странно, поскольку chapters() является методом экземпляра, а не методом класса. Это подтверждается последними двумя строками нашего примера, в которых мы создаем экземпляр экземпляра Book и задаем ему тот же вопрос.

В приведенном выше примере мы используем метод respondsToSelector(_:) , но вам может быть интересно, что такое селектор? Что значит «отвечать на селектор»? Селектор — это причудливое слово для имени функции или метода. Селектор используется средой выполнения Objective C для отправки сообщений объектам. Короче говоря, если вы читаете слово селектор, то подумайте о функции или методе. Более тонкие детали выходят за рамки этого урока.

Класс NSNumber — это служебный класс, который управляет любым из базовых числовых типов данных. Это подкласс класса NSValue , который предоставляет объектно-ориентированную оболочку для скалярных типов, указателей, структур и идентификаторов объектов. Класс NSNumber определяет методы для извлечения значения, которое он хранит, для сравнения значений, а также для возврата строкового представления сохраненного значения.

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

Позвольте мне проиллюстрировать это на примере. Добавьте следующий фрагмент кода на свою игровую площадку.

Работа с классом NSNumber

Мы создаем новый экземпляр NSNumber , передав значение Double в init(double:) . Затем мы запрашиваем хранимое значение, используя три разных метода NSNumber . Как видите, результаты не всегда отражают значение, хранящееся в myNumber .

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

Экземпляры класса NSString управляют массивом символов unichar образующих строку текста. Тонкое, но важное отличие обычной строки C, которая управляет символами char , заключается в том, что символ unichar является многобайтовым символом. Это похоже на String тип Swift.

Как следует из его названия, символ unichar идеально подходит для обработки символов Unicode. Благодаря этой реализации класс NSString обеспечивает NSString поддержку интернационализации.

Я хочу подчеркнуть, что строка, управляемая экземпляром NSString является неизменной. Это означает, что после создания строки ее нельзя изменить. Разработчики из других языков, таких как PHP, Ruby или JavaScript, могут быть смущены этим поведением.

Платформа Foundation также определяет изменяемый подкласс NSString , NSMutableString , который можно изменить после инициализации.

Существуют различные способы создания строковых объектов. Самый простой способ создать строковый объект — вызвать метод string() в классе NSString . Это возвращает пустой строковый объект. Посмотрите на ссылку на класс NSString для полного списка инициализаторов.

Другой распространенный путь для создания строковых объектов — через строковые литералы. В следующем примере строковый литерал присваивается stringA Во время компиляции компилятор заменит строковый литерал экземпляром NSString .

Создание строк в Swift

Обратите внимание, что мы не полагаемся на вывод типа Swift для определения типа stringA . Второй пример, stringB , показывает, почему это так. Если мы явно не указываем тип stringA как NSString , Swift думает, что мы хотим создать экземпляр String . В примере также показано, что NSString наследует от NSObject а String — нет. Помните, что тип String в Swift — это структура, а структуры не поддерживают наследование.

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

Давайте исследуем NSString и его изменяемый подкласс NSMutableString , добавив следующий фрагмент к вашей игровой площадке.

1
2
3
4
5
let stringA: NSString = «This is «
let stringB: NSString = NSString(string: «a mutable string.»)
 
var mutableString = NSMutableString(string: stringA)
mutableString.appendString(stringB as String)

Мы начнем с создания двух экземпляров NSString , один из которых использует буквальный синтаксис, а другой — init(string:) . Изменяемая строка затем создается путем передачи первой строки в качестве аргумента. Чтобы проиллюстрировать, что изменяемые строки могут быть изменены после создания, stringB добавляется к изменяемой строке и печатается ее значение.

Класс NSArray управляет неизменным упорядоченным списком объектов. Платформа Foundation также определяет изменяемый подкласс NSArray , NSMutableArray . Класс NSArray ведет себя очень похоже на массив C или Swift с той разницей, что экземпляр NSArray управляет объектами. Класс NSArray объявляет широкий спектр методов, облегчающих работу с массивами, таких как методы поиска и сортировки объектов в массиве.

Важно понимать, что экземпляры NSArray , NSSet и NSDictionary могут хранить только объекты. Это означает, что невозможно хранить скалярные типы, указатели или структуры ни в одном из этих классов коллекций — или их подклассах — компилятор выдаст ошибку, если вы это сделаете. Решение состоит в том, чтобы обернуть скалярные типы, указатели и структуры в экземпляр NSValue или NSNumber как мы видели ранее в этой статье.

Добавьте следующий фрагмент кода на игровую площадку, чтобы изучить NSArray и его изменяемый аналог NSMutableArray .

1
2
3
4
5
6
let myArray = NSArray(objects: «Bread», «Butter», «Milk», «Eggs»)
print(myArray.count)
print(myArray.objectAtIndex(2))
 
var myMutableArray = NSMutableArray(object: NSNumber(int: 265))
myMutableArray.addObject(NSNumber(int: 45))

Мы создаем массив с помощью init(objects:) . Этот метод принимает переменное количество аргументов. Далее мы печатаем количество объектов в массиве и запрашиваем у массива объект с индексом 2 .

Поскольку NSMutableArray наследуется от NSArray , он ведет себя во многом так же, как NSArray . Основное отличие состоит в том, что объекты могут быть добавлены и удалены из массива после инициализации.

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

Преимущество NSSet том, что запросы к его объектам выполняются быстрее, если вам нужно только знать, содержится ли объект в наборе. Основа также определяет NSOrderedSet . Экземпляры этого класса имеют преимущества NSSet , но также отслеживают положение каждого объекта.

Как и массивы, словари являются общей концепцией в большинстве языков программирования. Например, в Ruby они называются хешами. Основная концепция проста: словарь управляет статической коллекцией пар ключ-значение или записей.

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

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

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

Как и NSArray , платформа Foundation определяет изменяемый подкласс NSDictionary , NSMutableDictionary . Существуют различные способы создания словаря. Взгляните на следующий фрагмент кода.

1
2
3
4
5
6
let keyA: NSString = «myKey»
let keyB: NSString = «myKey»
 
let myDictionary = NSDictionary(object: «This is a string literal», forKey: keyA)
 
print(myDictionary.objectForKey(keyB))

Мы объявляем два отдельных объекта NSString содержащих одну и ту же строку. Затем мы создаем экземпляр словаря, вызывая init(object:forKey:) . Далее мы запрашиваем в словаре объект, связанный с содержимым keyB и печатаем его значение.

Важно обратить внимание на детали. Несмотря на то, что мы использовали keyA в качестве ключа пары ключ-значение и keyB в качестве ключа для извлечения значения или объекта пары ключ-значение, словарь дал нам правильный объект (как необязательный). Класс NSDictionary достаточно умен, чтобы знать, что мы хотим, чтобы объект был связан со строкой "myKey" . Что это значит? Даже если объекты keyA и keyB являются разными объектами, строка, которую они содержат, одинакова, и это именно то, что класс NSDictionary использует для ссылки на объект.

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

1
2
let myMutableDictionary = NSMutableDictionary()
myMutableDictionary.setObject(myDictionary, forKey: «myDictionary»)

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

В следующей статье мы рассмотрим UIKit-фреймворк, а также я расскажу о тонкостях iOS-приложения.

Если у вас есть какие-либо вопросы или комментарии, вы можете оставить их в комментариях ниже или обратиться ко мне в Twitter .