В чем разница между классами и структурами? Ведь они по сути похожи друг на друга. По умолчанию лучше использовать структуры, но почему? В каких случаях лучше использовать классы? В этой статье мы детально рассмотрим структуры и классы и разберем особенности их применения.
Структуры и классы
Сначала давайте посмотрим, что общего между классами и структурами:
- Они могут определять свойства и методы.
- Они могут определять сабскрипты.
- Они могут определить инициализаторы с помощью функции init().
- Они могут использовать расширения.
- Они могут соответствовать протоколам.
Также классы поддерживают дополнительные возможности, которые не имеют структуры:
- Классы могут наследоваться от другого класса.
- Классы могут содержать деинициализаторы.
- Классы являются ссылочными типами, а структуры являются значимыми типами.
В значимых типах, когда вы копируете значение, каждый экземпляр сохраняет уникальную копию данных. Если вы измените один экземпляр, другой экземпляр не изменится.
В ссылочных типах, когда вы копируете значение, копируется ссылка на область в памяти, в которой хранится данное значение. Каждый экземпляр совместно использует данные. Когда вы меняете один экземпляр, другой также меняется.
В Swift структуры являются значимыми типами, а классы являются ссылочными. Когда вы копируете структуру, вы получаете две уникальные копии данных. Когда вы копируете класс, вы получаете две ссылки на один экземпляр данных. Это принципиальное различие, и оно влияет на ваш выбор между классами или структурами.
Когда стоит использовать структуры?
По умолчанию рекомендуется использовать структуры. Структуры также полезны в следующих сценариях:
- Используйте структуры для простых типов данных. Воспринимайте их как простые базы данных, которые вы можете использовать в своем коде, например NewsItem, Task или User. Поскольку они четко определены и часто не нуждаются в сложных отношениях между объектами.
- В многопоточной среде, например, с подключением к базе данных в другом потоке, структуры более безопасны. и могут быть безопасно скопированы из одного потока в другой. Классы не имеют присущей им безопасности, если только они не намеренно сделаны поточно-ориентированными.
- Когда свойства структуры в основном являются значимыми типами, например String, имеет смысл обернуть их в структуру вместо класса.
Использование структур имеет дополнительное преимущество: вам проще анализировать изменения данных в вашем коде. Когда тип является структурой, вы можете быть уверены, что никакая другая часть вашего кода не сможет удерживать ссылку на объект. То есть структура не может быть изменена какой-либо другой частью вашего кода.
struct NewsItem { var title: String = "" var url: String = "" } var item = NewsItem() item.title = "Сравнение классов и структур в Swift" item.url = "https://developer.apple.com/documentation/" print(item) // NewsItem(title: "Сравнение классов и структур в Swift", url: "https://developer.apple.com/documentation/")
Когда стоит использовать классы?
Рекомендуется использовать класс, если вам нужны его характерные особенности. Как отмечалось ранее, у классов есть несколько дополнительных характеристик, которых нет у структур:
- Классы могут наследоваться друг от друга.
- Классы могут быть деинициализированы.
- Классы поставляются со встроенным понятием идентичности , потому что они являются ссылочными типами. С помощью оператора идентичности === вы можете проверить, ссылаются ли два экземпляра класса на один и тот же объект.
Рядом со ссылочным типом значений наследование является наиболее важным различием между классом и структурой. С помощью классов вы можете четко определить родительско-дочернюю связь между подклассом и суперклассом.
Несколько примеров:
- MyViewController наследуется от UIViewController.
- MyTableViewController наследуется от InfiniteTableViewController для создания «бесконечной прокрутки», который в свою очередь наследуется от UITableViewController.
- Классы Car и Bike наследуются от Vehicle, потому что оба они используют один и тот же «базовый» набор характеристик, что numberOfWheels и speed.
В двух последних примерах есть опасность: вы можете получить целую кучу унаследованных классов, у каждого из которых есть свои методы. В этом случае может иметь смысл использовать протокольно-ориентированное программирование.
Лучше использовать классы в следующих сценариях:
- Когда копирование экземпляров не имеет смысла, например, Window или UIViewController. Копировать окно приложения не имеет смысла, поскольку одновременно активно только один окно, и зачастую не имеет смысла также копировать View Controller – вы просто создаете новый.
- Когда время жизни экземпляра связано с внешними эффектами, например, для DatabaseConnection или TemporaryFile. Не имеет смысла создавать две копии ссылки на файл, если они оба ссылаются на одни и те же данные.
- Когда экземпляры просто являются проводниками для нешних состояний, например, CGContext. Иногда вам нужен вспомогательный класс или класс-оболочка, чтобы добиться цели. В этих случаях класс является всего лишь каналом, который передает информацию, и не имеет смысла создавать его копию.