Почему структуры NSRect, NSPoint и т. д., а не классы?

17

Мне недавно нужно было создать свой собственный тип, похожий на NSRect, который имеет опорную точку (по существу, NSRect с другим NSPoint в ней).

После некоторых research Я обнаружил, что на самом деле мне, вероятно, было бы лучше сделать этот класс (как в подклассе NSObject), а не использовать struct. Почему тогда Apple делает эти типы структурными, а не классами? Похоже, что у него будет много преимуществ, таких как отсутствие необходимости в их переносе в NSValues, не нужно использовать функции C для взаимодействия с ними и т. Д.

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

    
задан Oliver Cooper 06.04.2015 в 09:20
источник
  • Производительность? Динамическая отправка отличная, за исключением множества методов вызовов с очень маленькими телами. –  Jasper Blues 06.04.2015 в 10:24
  • Вы имеете в виду CGRect и CGPoint? Стоит отметить, что библиотека CoreGraphics предшествует использованию ObjC (NextStep), поэтому не была бы способна быть объектом при записи. –  OrangeDog 06.04.2015 в 15:19

9 ответов

12

Каждая парадигма программирования имеет свои плюсы и минусы и объектно-ориентированное программирование (ООП) не в исключение.

Все основные преимущества от наличия классов (то есть записи в объектно-ориентированном стиле): инкапсуляция , полиморфизм и наследование .

Если мы объявим такие простые единицы, как Rect или Point a class, мы не будем использовать что-либо из этого списка, но приведем все недостатки, которые имеет ООП, включая неявную изменчивость, более сложное управление памятью, ссылки на указатели, неявные участники, сломанная инкапсуляция (с сеттерами / геттерами) и т. д. И я не перечисляю здесь сотни анти-шаблонов , которые существуют в ООП.

Я сомневаюсь, что выбор структур для Rect и Point в Cocoa был оправдан C наследием, прежде всего потому, что Objective-C поддерживал классы с самого начала, а во-вторых, потому что новый Swift не имеет этого наследия, но все же почти все стандартные типы и контейнеры объявляются в его стандартной библиотеке как структуры, а не классы.

В общем случае, если вам не нужна инкапсуляция, полиморфизм и наследование - вам следует рассмотреть возможность объявления вашего нового объекта как структуры и, по возможности, избегать ООП.

    
ответ дан NAlexN 06.04.2015 в 09:54
  • Я не уверен, что это действительно решает вопрос. Структура Foundation предлагает функции для работы с такими структурами, как NSRect и NSPoint (а также NSSize, NSRange и т. Д.), Которые могут быть легко оправданы как методы. И если можно сказать, что классы Objective-C имеют «сломанную инкапсуляцию», то инкапсуляция в C-структурах действительно нарушена. Вы даже не можете сравнивать с структурами Swift, так как структуры Swift ближе к классам Objective-C, чем к C-структурам. –  mipadi 06.04.2015 в 21:04
  • В структурах C и Obj-C вообще нет инкапсуляции, поэтому, как я уже сказал, это не сломано, если вам это нужно в Obj-C, вы, вероятно, должны использовать классы. У структур есть другие сильные стороны. Что касается структур в Swift, то тот факт, что вы можете объявлять методы, именуемые «именами» для структуры, не означает, что структуры теперь могут быть похожими на объекты - они все еще очень разные. –  NAlexN 06.04.2015 в 21:29
  • Каким образом структуры Swift имеют какое-либо сходство с структурами C? –  mipadi 06.04.2015 в 21:31
  • Структуры представляют собой типы значений на обоих этих языках - это, вероятно, основное сходство. Классы - это ссылочные типы, как вы знаете. Я не утверждаю, что структуры в Swift могут делать гораздо больше, чем структуры на C. Вы получили тонны синтаксического сахара (что мне также нравится), но тем не менее семантика структуры не изменилась. –  NAlexN 06.04.2015 в 21:39
  • Для всех, кто прочитал этот комментарий, я бы рекомендовал смотреть видео с этой конференции. Очень вдохновляет использовать структуры. –  NAlexN 06.04.2015 в 21:48
Показать остальные комментарии
8

Главной причиной является производительность - все критически важные API Cocoa (графика, аудио) имеют тенденцию использовать C-структуры и функции C для этой цели.

Причины этого в том, что C-компилятор может быть намного легче оптимизирован. Функции C можно назвать более дешевыми, чем методы, и они могут быть встроены в компилятор автоматически, полностью устраняя служебные данные вызова функции. Структуры C выделяются в стеке вместо кучи, что означает, что обработка в основном может выполняться в регистрах или кэшах низкого уровня ЦП вместо вызова в основную память, что в сотни раз медленнее.

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

    
ответ дан Nick Lockwood 07.04.2015 в 11:25
4

Также хорошей точкой является скорость работы. Эти точки и прямоугольники в основном используются для визуализации чего-то и, следовательно, требуют более быстрой работы, чем когда-либо. Вместо выделения объекта, который вызовет рекурсивный init , потратив много байтов и циклов процессора для скрытой работы, просто сказать «здесь у меня есть 16 байтов, что является 2 CGFloats структуры NSPoint». Это также имеет большое значение при делегировании работы на графический процессор, который ничего не знает о ООП и методах ваших объектов, но нужно много рисовать на экране без сбоев.

    
ответ дан ReDetection 06.04.2015 в 18:45
  • Производительность почти наверняка является ответом. Эти проектные решения относятся к дням NeXT, когда компьютеры были намного менее мощными, а производительность - особенно в ключевом низкоуровневом чертежном коде - была критической. Конечно, трудно сказать, не разговаривая с людьми, которые на самом деле писали рамки. –  mipadi 06.04.2015 в 22:39
3
  

Почему тогда Apple делает эти типы structs, а не классы?

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

    
ответ дан Paolo 06.04.2015 в 10:12
  • На самом деле это не значит, почему некоторые типы данных, такие как NSRect, являются структурами, а другие - классами Objective-C. –  mipadi 06.04.2015 в 20:59
  • @mipadi Я согласен. Я неправильно понял вопрос ОП. (Я думал, что он говорит о классах C ++) –  Paolo 07.04.2015 в 16:53
2

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

    
ответ дан jamihash 06.04.2015 в 09:28
  • Как насчет таких функций, как «NSIntersectsRect», «NSPointFromString» и даже «NSMakeRect»? Не имеет смысла делать [myRect intersectsWithRect: otherRect]? –  Oliver Cooper 06.04.2015 в 09:34
  • Я согласен. Возможно, существуют устаревшие причины кода - я предполагаю. –  jamihash 06.04.2015 в 09:41
2

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

    
ответ дан Razvan 06.04.2015 в 10:59
  • Существует несколько функций, которые работают как на NSPoint, так и на NSRect, хотя это имело бы смысл как методы, поэтому я не уверен, что это причина. –  mipadi 06.04.2015 в 20:54
1

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

CGRect frame = ...
UIView *view1 = [[UIView alloc] initWithFrame:frame];

frame.origin = ...
UIView *view2 = [[UIView alloc] initWithFrame:frame];

И он отлично работает. С ссылочными типами, какие классы, вы измените кадр view1 при перемещении кадра в другое начало.

[myRect intersectsWithRect: otherRect] определенно было бы неплохо, и на самом деле Swift позволяет это делать. Такие типы значений, как структуры и перечисления, могут включать методы в Swift, что позволяет

    
ответ дан mrvn 06.04.2015 в 14:06
  • Это хороший момент, хотя решить его было бы нелегко. –  Oliver Cooper 06.04.2015 в 16:27
  • Конечно, но речь идет о различии между значением типа и объектом. Объект инкапсулирует данные (т. Е. Могут быть скрытые данные внутри объекта, которые не видны внешнему миру), struct просто сохраняет его. Объекты могут вести себя (т. Е. Изменять свои собственные свойства со временем), а типы значений не могут. realm.io/news/andy-matuschak-controlling-complexity проверьте этот разговор. Функции привязки к данным сами по себе хороши, но для этого вам необязательно нужен класс –  mrvn 06.04.2015 в 17:19
1

Если вы некоторое время играете с Objective-C, тогда вы будете знать, что это слой OOP поверх coreFoundation, который представляет собой все структуры и «непрозрачные типы» (структуры с секретами).

Например, NSDictionary - это просто обертка вокруг CFtype, CFDictionaryRef. Ну, то, о чем вы спрашиваете здесь, размер, точка, прямоугольник, edgeInsets и т. Д. - это всего лишь небольшие типы данных, которые были определены, чтобы не оправдывать обновление. Примитивы (int, double, bool и т. Д.) Также не получили (общедоступный) класс для бесплатного моста. Они получили кластер NSNumber, и все эти ребята были собраны вместе в обертке NSValue. Вы можете написать конкретный класс, чтобы обернуть конкретный, если вам нужно ...

    
ответ дан Jef 06.04.2015 в 11:26
0

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

ответ дан Omar Al-Shammary 06.04.2015 в 13:22