Почему стоит выбирать Struct Over Class?

Поэкспериментируя со Swift, исходя из фона Java, зачем вам выбирать Struct вместо Class? Похоже, что это одно и то же, а Struct предлагает меньше функциональности. Зачем тогда его выбирать?

18 ответов

Решение

В соответствии с очень популярным докладом WWDC 2015 по протоколу ориентированного программирования в Swift ( видео, транскрипция), Swift предоставляет ряд функций, которые во многих случаях делают структуры лучше, чем классы.

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

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

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

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

  • Копирование или сравнение экземпляров не имеет смысла (например, Window)
  • Время жизни экземпляра связано с внешними эффектами (например, TemporaryFile)
  • Экземпляры - это просто "приемники"- каналы только для записи во внешнее состояние (например, CGContext).

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

С другой стороны, документация по языку программирования Swift несколько противоречива:

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

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

  • Основной целью структуры является инкапсуляция нескольких относительно простых значений данных.
  • Разумно ожидать, что инкапсулированные значения будут копироваться, а не ссылаться на них при назначении или передаче экземпляра этой структуры.
  • Любые свойства, хранящиеся в структуре, сами по себе являются типами значений, которые также следует копировать, а не ссылаться на них.
  • Структура не должна наследовать свойства или поведение от другого существующего типа.

Примеры хороших кандидатов для структур включают в себя:

  • Размер геометрической фигуры, возможно, инкапсулируя свойство width и свойство height, оба типа Double.
  • Способ ссылки на диапазоны в серии, возможно, инкапсулируя свойство start и свойство length, оба типа Int.
  • Точка в трехмерной системе координат, возможно, инкапсулирующая свойства x, y и z, каждый из которых имеет тип Double.

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

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

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

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

Рассмотрим следующий пример, который демонстрирует 2 стратегии упаковки Int тип данных с использованием struct а также class, Я использую 10 повторяющихся значений, чтобы лучше отражать реальный мир, где у вас есть несколько полей.

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}

func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

Производительность измеряется с помощью

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}

// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}

func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()

    block()

    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

Код можно найти по адресу 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.159942142s
  • struct версия заняла 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.788332333s
  • struct версия заняла 0.010532942s (в 900 раз быстрее)

СТАРЫЕ РЕЗУЛЬТАТЫ (из неизвестного времени)

(Запускается на структуру / класс с 1 полем, а не 10)

С выпуском сборки на моем MacBook Pro:

  • class версия заняла 1.10082 сек
  • struct версия заняла 0,02324 сек (в 50 раз быстрее)

Сходства между структурами и классами.

Я создал суть для этого на простых примерах. https://github.com/objc-swift/swift-classes-vs-structures

И отличия

1. Наследование

структуры не могут наследовать в быстром. Если ты хочешь

class Vehicle{
}

class Car : Vehicle{
}

Пойти на урок

2. Пройдите мимо

Структуры Swift передаются по значению, а экземпляры классов - по ссылке.

Контекстные различия

Структурная константа и переменные

Пример (используется на WWDC 2014)

struct Point{

   var x = 0.0;
   var y = 0.0;

} 

Определяет структуру под названием Point.

var point = Point(x:0.0,y:2.0)

Теперь, если я попытаюсь изменить х. Это правильное выражение.

point.x = 5

Но если бы я определил точку как постоянную.

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

В этом случае вся точка является неизменной постоянной.

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

Предполагая, что мы знаем, что Struct является типом значения, а Class является ссылочным типом.

Если вы не знаете, что такое тип значения и ссылочный тип, посмотрите, в чем разница между передачей по ссылке и передачей по значению?

Основано на посте mikeash:

... Давайте сначала посмотрим на некоторые крайние, очевидные примеры. Целые числа явно копируемые. Они должны быть типами значений. Сетевые сокеты не могут быть разумно скопированы. Они должны быть ссылочными типами. Точки, как в парах x, y, копируются. Они должны быть типами значений. Контроллер, представляющий диск, не может быть разумно скопирован. Это должен быть ссылочный тип.

Некоторые типы могут быть скопированы, но это может не происходить постоянно. Это говорит о том, что они должны быть ссылочными типами. Например, кнопка на экране может быть концептуально скопирована. Копия не будет полностью идентична оригиналу. Нажатие на копию не активирует оригинал. Копия не будет занимать одно и то же место на экране. Если вы передадите кнопку или поместите ее в новую переменную, вы, вероятно, захотите сослаться на исходную кнопку, и вы захотите сделать копию только тогда, когда она явно запрошена. Это означает, что ваш тип кнопки должен быть ссылочным типом.

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

А как насчет типов моделей? У вас может быть тип User, представляющий пользователя в вашей системе, или тип Crime, представляющий действие, предпринятое пользователем. Они довольно копируемые, поэтому они должны быть типами значений. Однако вы, вероятно, хотите, чтобы обновления пользовательского преступления, сделанные в одном месте вашей программы, были видны другим частям программы. Это говорит о том, что вашими пользователями должны управлять какой-то пользовательский контроллер, который будет ссылочным типом.

Коллекции интересный случай. К ним относятся такие вещи, как массивы и словари, а также строки. Они копируемые? Очевидно. Легко и часто ли происходит копирование того, что вы хотите? Это менее понятно.

Большинство языков говорят "нет" на это и делают свои коллекции ссылочными типами. Это верно в Objective-C, Java, Python, JavaScript и почти во всех других языках, которые я могу себе представить. (Одним из главных исключений является C++ с типами коллекций STL, но C++ - бред сумасшедшего языкового мира, который делает все странно.)

Свифт сказал "да", что означает, что такие типы, как Array и Dictionary и String, являются структурами, а не классами. Они копируются при назначении и при передаче их в качестве параметров. Это вполне разумный выбор, если копия дешевая, чего Свифт очень старается выполнить....

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

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

Вот еще несколько причин для рассмотрения:

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

    struct MorphProperty {
       var type : MorphPropertyValueType
       var key : String
       var value : AnyObject
    
       enum MorphPropertyValueType {
           case String, Int, Double
       }
     }
    
     var m = MorphProperty(type: .Int, key: "what", value: "blah")
    

Чтобы получить это в классе, вы должны добавить инициализатор и поддерживать инициализатор...

  1. Основные типы коллекций, такие как Array являются структурами. Чем больше вы используете их в своем собственном коде, тем больше вы привыкнете передавать по значению, а не по ссылке. Например:

    func removeLast(var array:[String]) {
       array.removeLast()
       println(array) // [one, two]
    }
    
    var someArray = ["one", "two", "three"]
    removeLast(someArray)
    println(someArray) // [one, two, three]
    
  2. Очевидно, что неизменность и изменчивость - огромная тема, но многие умные люди считают, что неизменность - в данном случае структура - предпочтительнее. Изменчивые против неизменных объектов

Некоторые преимущества:

  • автоматически потокобезопасен из-за отсутствия общего доступа
  • использует меньше памяти из-за отсутствия isa и refcount (и фактически выделяется стек вообще)
  • методы всегда отправляются статически, поэтому могут быть встроенными (хотя @final может делать это для классов)
  • легче рассуждать (не нужно "защищенно копировать", как это обычно бывает с NSArray, NSString и т. д.) по той же причине, что и безопасность потоков

Structs являются value type а также Classes являются reference type

  • Типы значений быстрее, чем ссылочные типы
  • Экземпляры типа значения безопасны в многопоточной среде, поскольку несколько потоков могут изменять экземпляр, не беспокоясь о состоянии гонки или взаимоблокировках.
  • Тип значения не имеет ссылок в отличие от ссылочного типа; Поэтому нет утечек памяти.

Использовать value введите когда:

  • Вы хотите, чтобы копии имели независимое состояние, данные будут использоваться в коде в нескольких потоках

Использовать reference введите когда:

  • Вы хотите создать общее, изменяемое состояние.

Дополнительную информацию можно также найти в документации Apple

https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html


Дополнительная информация

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

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

В качестве напоминания вот список Swift

Типы значений:

  • Struct
  • Enum
  • Кортеж
  • Примитивы (Int, Double, Bool и т. Д.)
  • Коллекции (Массив, Строка, Словарь, Набор)

Типы ссылок:

  • Учебный класс
  • Все, что приходит от NSObject
  • функция
  • закрытие

Структура намного быстрее, чем класс. Кроме того, если вам нужно наследование, вы должны использовать класс. Наиболее важным моментом является то, что Class является ссылочным типом, тогда как Structure является типом значения. например,

class Flight {
    var id:Int?
    var description:String?
    var destination:String?
    var airlines:String?
    init(){
        id = 100
        description = "first ever flight of Virgin Airlines"
        destination = "london"
        airlines = "Virgin Airlines"
    } 
}

struct Flight2 {
    var id:Int
    var description:String
    var destination:String
    var airlines:String  
}

Теперь давайте создадим экземпляр обоих.

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

Теперь давайте передадим этот экземпляр двум функциям, которые изменяют идентификатор, описание, назначение и т. д.

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

также,

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

так,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

Теперь, если мы напечатаем идентификатор и описание рейса, мы получим

id = 200
description = "second flight of Virgin Airlines"

Здесь мы видим, что идентификатор и описание FlightA изменяются, поскольку параметр, передаваемый методу модификации, фактически указывает на адрес памяти объекта flightA (ссылочный тип).

Теперь, если мы напечатаем идентификатор и описание экземпляра FLightB, мы получим,

id = 100
description = "first ever flight of Virgin Airlines"

Здесь мы видим, что экземпляр FlightB не изменяется, потому что в методе modifyFlight2 фактический экземпляр Flight2 является скорее передачей, чем ссылкой (тип значения).

Отвечая на вопрос с точки зрения типов значений по сравнению с ссылочными типами, из этого поста в блоге Apple это будет выглядеть очень просто:

Используйте тип значения [например, struct, enum], когда:

  • Сравнение данных экземпляра с == имеет смысл
  • Вы хотите, чтобы копии имели независимое состояние
  • Данные будут использоваться в коде в нескольких потоках.

Используйте ссылочный тип [например, класс], когда:

  • Сравнение идентичности экземпляра с === имеет смысл
  • Вы хотите создать общее, изменяемое состояние

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

[Стек против кучи]

[Значение и тип ссылки]

Struct vs Class предпочтительнее.

Но Structпо умолчанию не решает всех проблем. Обычно это можно услышать value typeразмещается в стеке, но это не всегда так. В стеке размещаются только локальные переменные

      //simple blocks
struct ValueType {}
class ReferenceType {}

struct StructWithRef {
    let ref1 = ReferenceType()
}

class ClassWithRef {
    let ref1 = ReferenceType()
}

func foo() {
    
    //simple  blocks
    let valueType1 = ValueType()
    let refType1 = ReferenceType()
    
    //RetainCount
    //StructWithRef
    let structWithRef1 = StructWithRef()
    let structWithRef1Copy = structWithRef1
    
    print("original:", CFGetRetainCount(structWithRef1 as CFTypeRef)) //1
    print("ref1:", CFGetRetainCount(structWithRef1.ref1)) //2 (originally 3)
    
    //ClassWithRef
    let classWithRef1 = ClassWithRef()
    let classWithRef1Copy = classWithRef1
    
    print("original:", CFGetRetainCount(classWithRef1)) //2 (originally 3)
    print("ref1:", CFGetRetainCount(classWithRef1.ref1)) //1 (originally 2)
     
}

* Вы не должны использовать / полагаться на retainCount, потому что в нем нет полезной информации

Во время компиляции SIL(Swift Intermediate Language) может оптимизировать ваш код.

      swiftc -emit-silgen -<optimization> <file_name>.swift
//e.g.
swiftc -emit-silgen -Onone file.swift

//emit-silgen -> emit-sil(is used in any case)
//-emit-silgen           Emit raw SIL file(s)
//-emit-sil              Emit canonical SIL file(s)
//optimization: O, Osize, Onone. It is the same as Swift Compiler - Code Generation -> Optimization Level

Там вы можете найти <tcode id="50207768"></tcode>(размещение в стеке) и <tcode id="50207769"></tcode>(выделение в куче)

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

На Swift есть отличные сессии WWDC, на этот конкретный вопрос подробно дан ответ в одном из них. Обязательно смотрите их, так как это поможет вам быстрее освоить руководство по языку или iBook.

В Swift был введен новый паттерн программирования, известный как протоколно-ориентированное программирование.

Творческий Образец:

В Swift Struct представляет собой типы значений, которые автоматически клонируются. Поэтому мы получаем необходимое поведение для реализации шаблона прототипа бесплатно.

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


Мелкая копия дублирует только ссылку, которая указывает на эти объекты, тогда как глубокая копия дублирует ссылку на объект.


Реализация глубокого копирования для каждого ссылочного типа стала утомительной задачей. Если классы включают дополнительный ссылочный тип, мы должны реализовать шаблон прототипа для каждого из свойств ссылок. И тогда мы должны фактически скопировать весь граф объекта путем реализации NSCopying протокол.

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

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

Я бы не сказал, что структуры предлагают меньше функциональности.

Конечно, "я" является неизменным, кроме как в мутирующей функции, но это все.

Наследование работает хорошо, если вы придерживаетесь старой доброй идеи, что каждый класс должен быть абстрактным или окончательным.

Реализуйте абстрактные классы как протоколы, а финальные классы как структуры.

Хорошая вещь о структурах состоит в том, что вы можете сделать свои поля изменяемыми, не создавая общее изменяемое состояние, потому что копирование при записи позаботится об этом:)

Вот почему все свойства / поля в следующем примере являются изменяемыми, чего бы я не делал в Java, C# или классах swift.

Пример структуры наследования с небольшим грязным и простым использованием внизу в функции с именем "example":

protocol EventVisitor
{
    func visit(event: TimeEvent)
    func visit(event: StatusEvent)
}

protocol Event
{
    var ts: Int64 { get set }

    func accept(visitor: EventVisitor)
}

struct TimeEvent : Event
{
    var ts: Int64
    var time: Int64

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }
}

protocol StatusEventVisitor
{
    func visit(event: StatusLostStatusEvent)
    func visit(event: StatusChangedStatusEvent)
}

protocol StatusEvent : Event
{
    var deviceId: Int64 { get set }

    func accept(visitor: StatusEventVisitor)
}

struct StatusLostStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var reason: String

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

struct StatusChangedStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var newStatus: UInt32
    var oldStatus: UInt32

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

func readEvent(fd: Int) -> Event
{
    return TimeEvent(ts: 123, time: 56789)
}

func example()
{
    class Visitor : EventVisitor
    {
        var status: UInt32 = 3;

        func visit(event: TimeEvent)
        {
            print("A time event: \(event)")
        }

        func visit(event: StatusEvent)
        {
            print("A status event: \(event)")

            if let change = event as? StatusChangedStatusEvent
            {
                status = change.newStatus
            }
        }
    }

    let visitor = Visitor()

    readEvent(1).accept(visitor)

    print("status: \(visitor.status)")
}

Многие API-интерфейсы Какао требуют подклассов NSObject, что заставляет вас использовать класс. Но кроме этого, вы можете использовать следующие случаи из блога Apple Swift, чтобы решить, использовать ли тип значения struct / enum или ссылочный тип класса.

https://developer.apple.com/swift/blog/?id=10

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

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

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

Недостатки структуры в том, что она не может быть унаследована.

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

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

  • Используйте структуры по умолчанию.
  • Используйте классы, когда вам нужна совместимость с Objective-C.
  • Используйте классы, когда вам нужно контролировать идентичность данных, которые вы моделируете.
  • Используйте структуры вместе с протоколами для адаптации поведения путем совместного использования реализаций.

Ссылка: документация Apple.

  • Структура и класс являются определяемыми пользователем типами данных

  • По умолчанию структура является общедоступной, тогда как класс является частным

  • Класс реализует принцип инкапсуляции

  • Объекты класса создаются в памяти кучи

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

  • Элементы данных структуры не могут быть инициализированы напрямую, но они могут быть назначены извне структуры

  • Члены класса данных могут быть инициализированы непосредственно конструктором без параметров и назначены параметризованным конструктором

Другие вопросы по тегам