Вопрос

В Ruby есть модули, и вы можете расширить класс, «подмешивая» модуль.

module MyModule
  def printone
    print "one" 
  end
end

class MyClass
  include MyModule
end

theOne = MyClass.new
theOne.printone 
>> one

В Objective-C я обнаружил, что у меня есть набор общих методов, которые я хочу, чтобы несколько классов «наследовали».Какими еще способами я могу добиться этого, не создавая общий класс и не наследуя все от этого общего класса?

Это было полезно?

Решение

Редактировать:изменения добавлены, потому что некоторые люди считают, что я несу ответственность за ограничения Objective-C.

Короткий ответ:ты не можешь.Objective-C не имеет эквивалента примесей Ruby.

Чуть менее короткий ответ:В Objective-C есть нечто похожее:протоколы.Протоколы (интерфейсы на некоторых других языках) — это способ определить набор методов, которые класс принимает и которые протоколы обязуются реализовать.Однако протокол не обеспечивает реализацию.Это ограничение не позволяет использовать протоколы как точный эквивалент миксинов Ruby.

Еще менее короткий ответ: Однако среда выполнения Objective-C имеет открытый API, который позволяет вам экспериментировать с динамическими функциями языка.Тогда вы выходите за пределы языка, но у вас могут быть протоколы с реализациями по умолчанию (также называемые конкретными протоколами).Ответ Владимира показывает один из способов сделать это.На этом этапе мне кажется, что вы уже поняли миксины Ruby.

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

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

  • Вы зависите от что реализация языка.Конечно, платформы Apple на сегодняшний день являются наиболее распространенными для Objective-C, но не забывайте о Cocotron или GnuStep (или Etoilé), которые имеют разные среды выполнения, которые могут быть совместимы, а могут и не быть совместимы с платформами Apple в этом отношении.

В качестве примечания ниже я заявляю, что категории не могут добавлять состояние (переменные экземпляра) в класс.Используя API среды выполнения, вы также можете снять это ограничение.Однако это выходит за рамки этого ответа.

Длинный ответ:

Две функции Objective-C выглядят как возможные кандидаты:категории и протоколы.Категории здесь не совсем правильный выбор, если я правильно понимаю вопрос.Правильная функция — протокол.

Позвольте мне привести пример.Предположим, вы хотите, чтобы у нескольких ваших классов была особая способность, называемая «петь».Затем вы определяете протокол:

@protocol Singer
    - (void) sing;
@end

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

@interface Rectangle : Shape <Singer> {
    <snip>
@end

@interface Car : Vehicle <Singer> {
    <snip>
@end

Заявляя о принятии протокола, они обязуются выполнять sing метод.Например:

@implementation Rectangle

- (void) sing {
    [self flashInBrightColors];
}

@end

@implementation Car

- (void) sing {
    [self honk];
}

@end

Затем вы используете эти классы, например, так:

void choral(NSArray *choir) // the choir holds any kind of singer
{
    id<Singer> aSinger;
    for (aSinger in choir) {
        [aSinger sing];
    }
}

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

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

Это самое главное.Однако давайте упомянем категории.

Категория объявляется не в угловых скобках, а в круглых скобках.Разница в том, что категорию можно определить для существующего класса, чтобы расширить его без создания подкласса.Вы можете сделать это даже для системного класса.Как вы понимаете, категории можно использовать для реализации чего-то похожего на миксин.И они использовались таким образом в течение долгого времени, обычно как категория для NSObject (типичный корень иерархии наследования) до такой степени, что их стали называть «неформальными» протоколами.

Это неформально, потому что 1 — компилятор не выполняет проверку типов, а 2 — реализация методов протокола необязательна.

Сегодня нет необходимости использовать категории в качестве протоколов, особенно потому, что формальные протоколы теперь могут объявлять некоторые из своих методов необязательными с помощью ключевого слова @optional или требуется (по умолчанию) с @required.

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

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

Другие советы

Бесстыжая пробка: ЦельMixin

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

Вы можете буквально смешивать код, используя #include.Это не рекомендуется и противоречит всем религиям в Objective-C, однако работает отлично.

Пожалуйста, не делайте этого в производственном коде.

например в файле:

MixinModule.header (не следует компилировать или копировать в цель)

-(void)hello;

MixinModule.body (не следует компилировать или копировать в цель)

-(void)hello{
    NSLog(@"Hello");
}

в классе миксина:

@interface MixinTest : NSObject
#include "MixinModule.header"
@end

@implementation MixinTest
#include "MixinModule.body"
@end

случай использования:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]){
    @autoreleasepool {
        [[[MixinTest new] autorelease] hello];
    }
    return 0;
}

Пожалуйста, не делайте этого в производственном коде.

Это мой взгляд на реализацию миксинов в Objective-C без непосредственного использования среды выполнения Objective-C.Возможно, кому-то будет полезно: https://stackoverflow.com/a/19661059/171933

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top