Ну просто я сейчас прохожу лекцию. И тут приводят аргумент, что преимущества структуры заключается в том, что она не наследуется и соответсвенно не перенимает лишние методы и свойства. Но класс же тоже можно не наследовать и просто написать новый
не то, чтобы можно думать о классах или структурах в категориях "лучше" или "хуже". Они просто разные и каждый тип хорош для своей задачи
Еще можно добавить такое понятие как value-type семантика (гуглится)
- можно управлять мутабельностью через var/let,
- а значит можно получить гарантии, что значение не будет изменено из вне (ценное свойство для структур с данными)
Храняться еще структуры (value-type) на стеке, а классы (reference type) в куче. Получение значения и стека работает быстрей, потому что в стеке все значения рядышком в памяти храняться (+ оптимизации на ядре), а в куче они рандомно в памяти разбрасоны и процессор не может применить никакие оптимизация для скорого доступа к объектам в куче.
Еще можно про скорость поговорить. При работе с классами Swift'у приходиться менеджить жизненый цикл value (считать ссылки на объект, деструктить его, если reference counter стал равен 0) и занимать выделением памяти.
- и вроде операция инкремента счетчика ссылов не супер затратная, но их прям оч много надо делать (гуглить reference counting overhead и retain/release overhead)
- к тому же оперцию подсчета ссылок на объект надо синкронизировать между разными потоками, что делает ее еще более медленной
И еще про скорость. Есть такая тема Method Dispatch (лучше найти сессию на WWDC). Это тема про то, как определить какую реализацию метода вообще надо выполнить при вызове функции Foo(). У класса то есть наследование и функции можно переопределять. И если у тебя массив cars: [Vehicle], но в нем лежат лежат объекты классов Bus, Plane, Bicycle, то когда ты будешь в цикле по элементам массива пробегать и вызывать функцию Foo() у каждого (которая может переопределена, а может и нет), то по факту Свифт не знает какую реализацию выполнить. И ему приходиться в рантайме определять, что нужно выполнить — а это долго. Со структура проще — нет наследования, нет и переопределения (если это не протоколы конечно), а значит реализация Foo() находится еще в compile time и в рантайме время на поиск реализации тратить не надо.
И у классов еще, есть такое понятие – identity (часть reference type семантики). Т.е. объекты классов можно сравнить на равенство по указателю (адресу в памяти) -> если адрес совпадает, то эти 2 переменные равны (т.к. это просто 2 разные ссылки, которые указывают на один и тот же объект)
И переменные классов можно менять просто, типа classObject.value = 2. Swift идет по ссылке в кучу и меняет значение. Это свойсто Apple именует indirect storage (еще часть reference type семантики). Со структурой по другому — если меняешь значение у проперти, то создается новая структура и инициализируется сразу с ообновленным значением проперти, и после создания новая структура присваивается в твою переменную.
Звучит наверно долго по времени? Это ж надо копировать, инициализировать....
Но не совсем уж и долго. И чтобы понять почему не долго, надо рассмотреть Array например или String. И Array и String структуры, но вот контент (элементы массива и char'ы соответственно) храняться в куче. Поэтому когда создается копия Array, то под капотом Swift только создает небольшую оболочку для новой структуры Array/String/Data, а контент так и остается в куче лежать и на него новой копии структуры просто ссылка дается
P.s. такая комбинация получается value type и reference type, где от каждого взято только самое лучше))