В чем разница между наследованием и категориями в Objective-C - PullRequest
57 голосов
/ 07 февраля 2009

Может ли кто-нибудь объяснить мне разницу между категориями и наследованием в Цели C? Я прочитал запись в Википедии , и обсуждение категорий там ничем не отличается от обсуждения наследования. Я также посмотрел обсуждение этой темы в книге «Разработка открытого iPhone» и до сих пор не понимаю.

Ответы [ 7 ]

96 голосов
/ 07 февраля 2009

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

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

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

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

Редактировать январь 2012

Все изменилось. С текущим компилятором LLVM и современной 64-битной средой исполнения вы можете добавить iVars и свойства к классу extensions (не по категориям). Это позволяет вам держать частные iVars вне публичного интерфейса. Но если вы объявите свойства для iVars, они все равно могут быть доступны / изменены через KVC, потому что в Objective-C до сих пор не существует такого понятия, как закрытый метод.

17 голосов
/ 07 февраля 2009

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

Чтобы увидеть, насколько это может быть полезно, посмотрите: CocoaDev

11 голосов
/ 09 декабря 2009

Одной из любимых иллюстраций категорий Objective-c в действии является NSString. NSString определяется в платформе Foundation, которая не имеет представления о представлениях или окнах. Однако, если вы используете NSString в приложении Какао, вы заметите, что оно отвечает на сообщения типа – drawInRect:withAttributes:.

AppKit определяет категорию для NSString, которая предоставляет дополнительные методы рисования. Категория позволяет добавлять новые методы в существующий класс, поэтому мы все еще имеем дело с NSStrings. Если бы вместо этого AppKit реализовал рисование путем создания подклассов, нам пришлось бы иметь дело с «AppKitStrings» или «NSSDrawableStrings» или чем-то подобным.

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

4 голосов
/ 24 мая 2012

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

К сожалению, это не всегда так или даже желательно. Часто вам дают набор двоичных библиотек / объектов и набор заголовков.

Затем для класса требуется новая функциональность, чтобы вы могли сделать пару вещей:

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

  2. создайте новый класс-обертку, который содержит класс stock в качестве члена (композитинг), и переписайте кодовую базу, чтобы использовать новый класс.

  3. бинарные патчи библиотеки для изменения кода (удачи)

  4. заставит компилятор видеть ваш новый класс как старый и надеяться, что он не зависит от определенного размера или места в памяти и конкретных точек входа.

  5. специализация подкласса - создайте подклассы для добавления функциональности и измените код драйвера для использования подкласса - теоретически проблем должно быть немного, и если вам необходимо добавить элементы данных, это необходимо, но объем памяти будет разные. У вас есть преимущество в том, что в подклассе есть и новый код, и старый код, и вы можете выбрать, какой из них использовать: метод базового класса или переопределенный метод.

  6. измените необходимый класс objc с определением категории, содержащим методы, чтобы делать то, что вы хотите, и / или переопределить старые методы в классах акций.

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

    пример:

    У вас есть класс Bing, который выводит на терминал, но не на последовательный порт, и теперь это то, что вам нужно. (по какой-то причине). У вас есть Bing.h и libBing.so, но нет Bing.m в вашем наборе.

    Класс Bing выполняет все внутри себя, вы даже не знаете что, у вас просто есть общедоступный API в заголовке.

    Вы умны, поэтому вы создаете категорию (SerialOutput) для класса Bing.

    [Bing_SerialOutput.m]
    @interface Bing (SerialOutput)   // a category
    - (void)ToSerial: (SerialPort*) port ;
    @end
    
    @implementation Bing (SerialOutput)
    - (void)ToSerial: (SerialPort*) port 
    {
    ... /// serial output code ///
    }
    @end
    

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

3 голосов
/ 27 июля 2014

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

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

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

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

Кто-нибудь знает, есть ли ситуации, когда категории абсолютно необходимы?

2 голосов
/ 07 февраля 2009

Категория похожа на миксин: модуль в Ruby или похож на интерфейс в Java. Вы можете думать об этом как о «голых методах». Когда вы добавляете категорию, вы добавляете методы в класс. В статье Википедии есть хорошие вещи .

0 голосов
/ 06 июля 2012

Лучший способ увидеть эту разницу заключается в том, что: 1. наследование: когда хочешь повернуть это точно по-своему. пример: AsyncImageView для реализации отложенной загрузки. Что делается путем наследования UIView. 2. Категория: Просто хочу добавить дополнительный вкус к нему. пример: мы хотим заменить все пробелы в текстовом поле текста

   @interface UITextField(setText)
      - (NSString *)replaceEscape;
   @end

   @implementation UITextField(setText)
      - (NSString *)replaceEscape
      {
         self.text=[self.text stringByTrimmingCharactersInSet:
                           [NSCharacterSet whitespaceCharacterSet]];
         return self.text;
      }
   @end

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...