Поэкспериментируя со Swift, исходящим из Java, зачем вам выбирать Struct вместо Class? Похоже, что это одно и то же, а Struct предлагает меньше функциональности. Зачем тогда выбирать?
swift
class
struct
design-principles
bluedevil2k
источник
источник
Ответы:
В соответствии с очень популярным докладом о программировании на языке WWDC 2015 в Swift ( видео , транскрипция ), Swift предоставляет ряд функций, которые во многих случаях делают структуры лучше, чем классы.
Структуры предпочтительнее, если они относительно малы и копируемы, потому что копирование намного безопаснее, чем использование нескольких ссылок на один и тот же экземпляр, как это происходит с классами. Это особенно важно при передаче переменной во многие классы и / или в многопоточной среде. Если вы всегда можете отправить копию своей переменной в другие места, вам никогда не придется беспокоиться о том, что это другое место изменит значение вашей переменной под вами.
В случае Structs гораздо меньше нужно беспокоиться об утечках памяти или множественных потоках, пытающихся получить доступ / изменить один экземпляр переменной. (Для более технически мыслящих, исключение составляет случай захвата структуры внутри замыкания, потому что тогда он фактически захватывает ссылку на экземпляр, если вы явно не пометите его для копирования).
Классы также могут стать раздутыми, потому что класс может наследовать только от одного суперкласса. Это побуждает нас создавать огромные суперклассы, которые включают в себя множество различных способностей, которые слабо связаны между собой. Использование протоколов, особенно с расширениями протоколов, где вы можете предоставлять реализации протоколов, позволяет вам исключить необходимость в классах для достижения такого поведения.
В докладе изложены следующие сценарии, где предпочтение отдается классам:
Это подразумевает, что структуры должны быть по умолчанию, а классы должны быть запасным вариантом.
С другой стороны, документация по языку программирования Swift несколько противоречива:
Здесь утверждается, что мы должны по умолчанию использовать классы и использовать структуры только в определенных обстоятельствах. В конечном счете, вам необходимо понять реальное значение типов значений по сравнению с ссылочными типами, и тогда вы сможете принять обоснованное решение о том, когда использовать структуры или классы. Кроме того, имейте в виду, что эти концепции постоянно развиваются, и документация по Swift Programming Language была написана до того, как была сделана речь о протоколно-ориентированном программировании.
источник
In practice, this means that most custom data constructs should be classes, not structures.
Можете ли вы объяснить мне, как, прочитав это, вы получите, что большинство наборов данных должны быть структурами, а не классами? Они дали определенный набор правил, когда что-то должно быть структурой, и в значительной степени сказали: «Во всех других сценариях класс лучше».Поскольку экземпляры структуры размещаются в стеке, а экземпляры классов размещаются в куче, структуры иногда могут быть значительно быстрее.
Тем не менее, вы всегда должны измерить его самостоятельно и принять решение на основе вашего уникального варианта использования.
Рассмотрим следующий пример, который демонстрирует 2 стратегии переноса
Int
типов данных с использованиемstruct
иclass
. Я использую 10 повторяющихся значений, чтобы лучше отражать реальный мир, где у вас есть несколько полей.Производительность измеряется с помощью
Код можно найти по адресу https://github.com/knguyen2708/StructVsClassPerformance
ОБНОВЛЕНИЕ (27 марта 2018 года) :
Начиная с Swift 4.0, Xcode 9.2, с выпуском сборки на iPhone 6S, iOS 11.2.6, настройка компилятора Swift
-O -whole-module-optimization
:class
версия заняла 2,06 секундыstruct
версия заняла 4.17e-08 секунд (в 50 000 000 раз быстрее)(Я больше не усредняю множественные прогоны, так как отклонения очень малы, менее 5%)
Примечание : разница намного менее существенна без оптимизации всего модуля. Я был бы рад, если бы кто-то мог указать, что на самом деле делает флаг.
ОБНОВЛЕНИЕ (7 мая 2016 года) :
Начиная с Swift 2.2.1, Xcode 7.3, работающий с выпуском сборки на iPhone 6S, iOS 9.3.1, в среднем за 5 запусков, настройка компилятора Swift
-O -whole-module-optimization
:class
версия заняла 2.159942142sstruct
версия заняла 5.83E-08s (в 37 000 000 раз быстрее)Примечание : как кто-то упомянул, что в реальных сценариях, вероятно, будет более 1 поля в структуре, я добавил тесты для структур / классов с 10 полями вместо 1. Удивительно, но результаты не сильно различаются.
ОРИГИНАЛЬНЫЕ РЕЗУЛЬТАТЫ (1 июня 2014 г.):
(Запускается на структуру / класс с 1 полем, а не 10)
Начиная с Swift 1.2, Xcode 6.3.2, работающий под управлением Release build на iPhone 5S, iOS 8.3, в среднем за 5 прогонов
class
версия заняла 9.788332333sstruct
версия заняла 0.010532942s (в 900 раз быстрее)СТАРЫЕ РЕЗУЛЬТАТЫ (из неизвестного времени)
(Запускается на структуру / класс с 1 полем, а не 10)
С выпуском сборки на моем MacBook Pro:
class
Версия приняла 1.10082 секstruct
Версия приняла 0.02324 сек ( в 50 раз быстрее)источник
Сходства между структурами и классами.
Я создал суть для этого на простых примерах. https://github.com/objc-swift/swift-classes-vs-structures
И отличия
1. Наследование
структуры не могут наследовать быстро. Если хочешь
Пойти на урок
2. Пройдите мимо
Структуры Swift передаются по значению, а экземпляры классов - по ссылке.
Контекстные различия
Структурная константа и переменные
Пример (используется на WWDC 2014)
Определяет структуру под названием Point.
Теперь, если я попытаюсь изменить х. Это правильное выражение.
Но если бы я определил точку как постоянную.
В этом случае вся точка является неизменной постоянной.
Если бы я использовал класс Point вместо этого, это правильное выражение. Потому что в классе неизменяемой константой является ссылка на сам класс, а не на его переменные экземпляра (если только эти переменные не определены как константы)
источник
Вот еще несколько причин для рассмотрения:
структуры получают автоматический инициализатор, который вам вообще не нужно поддерживать в коде.
Чтобы получить это в классе, вы должны добавить инициализатор и поддерживать инициализатор ...
Основные типы коллекций, такие
Array
как структуры. Чем больше вы используете их в своем собственном коде, тем больше вы привыкнете передавать по значению, а не по ссылке. Например:Очевидно, что неизменность и изменчивость - огромная тема, но многие умные люди думают, что неизменность - структуры в этом случае - предпочтительнее. Изменчивые против неизменных объектов
источник
internal
области.mutating
чтобы вы четко указывали, какие функции изменяют свое состояние. Но их природа как ценностных типов - вот что важно. Если вы объявляете структуру,let
вы не можете вызывать какие-либо изменяющие функции для нее. Видео WWDC 15 о Лучшем программировании через типы значений является отличным ресурсом по этому вопросу.Предполагая, что мы знаем, что Struct является типом значения, а Class является ссылочным типом .
Если вы не знаете, что такое тип значения и ссылочный тип, посмотрите, в чем разница между передачей по ссылке и передачей по значению?
Основано на посте mikeash :
Я лично так не называю свои занятия. Я обычно называю мой UserManager вместо UserController, но идея та же
Кроме того, не используйте класс, когда вам нужно переопределить каждый экземпляр функции, то есть они не имеют общей функциональности.
Таким образом, вместо нескольких подклассов класса. Используйте несколько структур, которые соответствуют протоколу.
Другой разумный аргумент для структур - это когда вы хотите выполнить различие между старой и новой моделью. С типами ссылок вы не можете сделать это из коробки. С типами значений мутации не являются общими.
источник
Некоторые преимущества:
источник
Структура намного быстрее, чем класс. Кроме того, если вам нужно наследование, вы должны использовать класс. Наиболее важным моментом является то, что Class является ссылочным типом, тогда как Structure является типом значения. например,
Теперь давайте создадим экземпляр обоих.
Теперь давайте передадим этот экземпляр двум функциям, которые изменяют идентификатор, описание, назначение и т. д.
также,
так,
Теперь, если мы напечатаем идентификатор и описание рейса, мы получим
Здесь мы видим, что идентификатор и описание FlightA изменяются, поскольку параметр, передаваемый методу модификации, фактически указывает на адрес памяти объекта flightA (ссылочный тип).
Теперь, если мы напечатаем идентификатор и описание экземпляра FLightB, мы получим,
Здесь мы видим, что экземпляр FlightB не изменяется, потому что в методе modifyFlight2 фактический экземпляр Flight2 является скорее передачей, чем ссылкой (тип значения).
источник
Here we can see that the FlightB instance is not changed
Structs
естьvalue type
иClasses
естьreference type
Используйте
value
тип, когда:Используйте
reference
тип, когда:Дополнительную информацию можно также найти в документации Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
Дополнительная информация
Типы значений Swift хранятся в стеке. В процессе каждый поток имеет свое собственное пространство стека, поэтому никакой другой поток не сможет напрямую получить доступ к вашему типу значения. Следовательно, нет условий гонки, блокировок, взаимоблокировок или какой-либо связанной сложности синхронизации потоков.
Типы значений не требуют динамического выделения памяти или подсчета ссылок, которые являются дорогостоящими операциями. При этом методы по типам значений рассылаются статически. Это создает огромное преимущество в пользу типов значений с точки зрения производительности.
В качестве напоминания вот список Swift
Типы значений:
Типы ссылок:
источник
Отвечая на вопрос с точки зрения типов значений по сравнению со ссылочными типами, из этого поста в блоге Apple это будет выглядеть очень просто:
Как упоминалось в этой статье, класс без доступных для записи свойств будет вести себя идентично со структурой, с (я добавлю) одним предупреждением: структуры лучше всего подходят для поточно-ориентированных моделей - все более неизбежное требование в современной архитектуре приложения.
источник
С классами вы получаете наследование и передаете по ссылке, структуры не имеют наследования и передаются по значению.
На Swift есть отличные сессии WWDC, на этот конкретный вопрос подробно дан ответ в одном из них. Обязательно смотрите их, так как это поможет вам быстрее освоить руководство по языку или iBook.
источник
Я бы не сказал, что структуры предлагают меньше функциональности.
Конечно, «я» является неизменным, кроме как в мутирующей функции, но это все.
Наследование работает хорошо, пока вы придерживаетесь старой доброй идеи, что каждый класс должен быть абстрактным или окончательным.
Реализуйте абстрактные классы как протоколы, а финальные классы как структуры.
Хорошая вещь о структурах состоит в том, что вы можете сделать ваши поля изменяемыми, не создавая общее изменяемое состояние, потому что копирование при записи позаботится об этом :)
Вот почему все свойства / поля в следующем примере являются изменяемыми, чего я бы не делал в классах Java, C # или swift .
Пример структуры наследования с небольшим грязным и простым использованием внизу в функции с именем «example»:
источник
Творческий Образец:
В Swift Struct представляет собой типы значений, которые автоматически клонируются. Поэтому мы получаем необходимое поведение для реализации шаблона прототипа бесплатно.
Принимая во внимание, что классы являются ссылочным типом, который не клонируется автоматически во время присваивания. Чтобы реализовать шаблон прототипа, классы должны принять
NSCopying
протокол.Мелкая копия дублирует только ссылку, которая указывает на эти объекты, тогда как глубокая копия дублирует ссылку на объект.
Реализация глубокого копирования для каждого ссылочного типа стала утомительной задачей. Если классы включают дополнительный ссылочный тип, мы должны реализовать шаблон прототипа для каждого из свойств ссылок. И затем мы должны фактически скопировать весь граф объекта путем реализации
NSCopying
протокола.Используя структуры и перечисления , мы упростили наш код, поскольку нам не нужно реализовывать логику копирования.
источник
Многие API-интерфейсы Какао требуют подклассов NSObject, что заставляет вас использовать класс. Но кроме этого, вы можете использовать следующие случаи из блога Apple Swift, чтобы решить, использовать ли тип значения struct / enum или ссылочный тип класса.
https://developer.apple.com/swift/blog/?id=10
источник
Один момент, на который не обращают внимания в этих ответах, заключается в том, что переменная, содержащая класс и структуру, может еще некоторое
let
время разрешать изменения свойств объекта, в то время как вы не можете сделать это с помощью структуры.Это полезно, если вы не хотите, чтобы переменная когда-либо указывала на другой объект, но все же нужно изменить объект, то есть в случае наличия множества переменных экземпляра, которые вы хотите обновить одну за другой. Если это структура, вы должны разрешить сброс переменной в другой объект в целом, используя
var
для этого, поскольку тип постоянного значения в Swift должным образом допускает нулевую мутацию, в то время как ссылочные типы (классы) не ведут себя таким образом.источник
Поскольку структура является типом значений, вы можете очень легко создать память, которая хранится в стеке. Struct может быть легко доступен, и после объема работы он легко освобождается из памяти стека посредством всплывающего сообщения с вершины стека. С другой стороны, класс является ссылочным типом, который хранится в куче, и изменения, внесенные в один объект класса, будут влиять на другой объект, так как они тесно связаны и ссылаются на тип. Все члены структуры являются открытыми, тогда как все члены класса являются частными ,
Недостатки структуры в том, что она не может быть унаследована.
источник
Структура и класс являются определяемыми пользователем типами данных
По умолчанию структура является общедоступной, тогда как класс является частным
Класс реализует принцип инкапсуляции
Объекты класса создаются в памяти кучи
Класс используется для повторного использования, в то время как структура используется для группировки данных в одной структуре.
Элементы данных структуры не могут быть инициализированы напрямую, но они могут быть назначены извне структуры
Члены класса данных могут быть инициализированы непосредственно конструктором без параметров и назначены параметризованным конструктором
источник