Определение категорий для протоколов в Objective-C?

В Objective-C я могу добавлять методы к существующим classам с категорией, например

@interface NSString (MyCategory) - (BOOL) startsWith: (NSString*) prefix; @end 

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

 @interface  (MyCategory) - (BOOL) startsWith: (NSString*) prefix; @end 

Я хочу сделать это, так как у меня есть несколько расширений для NSObject (class), используя только общедоступные методы NSObject, и я хочу, чтобы эти расширения также работали с объектами, реализующими протокол.

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

 - (void) logDescription { NSLog(@"%@", [self description]); } 

Я могу, конечно, добавить этот метод в NSObject, но есть другие classы, которые не наследуются от NSObject, где я также хотел бы иметь этот метод, например NSProxy. Поскольку метод использует только публичные члены протокола, лучше всего добавить его в протокол.

Изменить: Java 8 теперь имеет это с помощью «методов виртуального расширения» в интерфейсах: http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf . Это именно то, что я хотел бы сделать в Objective-C. Я не видел, чтобы этот вопрос привлек это внимание …

С уважением, Йохен

    extObjC имеет NEATEST материал, который вы можете сделать с протоколами / категориями … first off is @concreteprotocol

    • Определяет «конкретный протокол», который может предоставлять стандартные реализации методов в протоколе.
    • Блок @protocol должен существовать в файле заголовка и соответствующий блок @concreteprotocol в файле реализации.
    • Любой объект, объявляющий себя соответствующим этому протоколу, получит свои реализации метода, но только если не существует метода с тем же именем.

    MyProtocol.h

     @protocol MyProtocol @required - (void)someRequiredMethod; @optional - (void)someOptionalMethod; @concrete - (BOOL)isConcrete; 

    MyProtocol.m

      @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ... 

    поэтому объявление объекта MyDumbObject : NSObject автоматически вернет YES в isConcrete .

    Кроме того, у них есть pcategoryinterface(PROTOCOL,CATEGORY) который «определяет интерфейс для категории CATEGORY на протоколе PROTOCOL». Категории протокола содержат методы, которые автоматически применяются к любому classу, который объявляет себя соответствующим PROTOCOL. «В вашем файле реализации есть также макрос, который вы также должны использовать. См. Документы.

    Последнее, но не в последнюю очередь / не связано напрямую с @protocols , synthesizeAssociation(CLASS, PROPERTY) @protocols synthesizeAssociation(CLASS, PROPERTY) , которая «синтезирует свойство для classа с использованием связанных объектов. Это в первую очередь полезно для добавления свойств в class внутри категории. объявленный с помощью @property в интерфейсе указанного classа (или категории на нем) и должен быть типа объекта. ”

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

    Краткий ответ: Нет.

    Долгий ответ: как это будет работать? Представьте, что вы могли бы добавлять методы к существующим протоколам? Как это будет работать? Представьте, что мы хотели добавить еще один метод для NSCoding, скажем -(NSArray *) codingKeys; Этот метод является обязательным методом, который возвращает массив ключей, используемых для кодирования объекта.

    Проблема в том, что существуют существующие classы (например, NSString), которые уже реализуют NSCoding, но не реализуют наш метод codingKeys . Что должно произойти? Каким образом предварительно скомпилированная структура знает, что делать, когда это необходимое сообщение отправляется classу, который его не реализует?

    Вы могли бы сказать: «Мы можем добавить определение этого метода через категорию» или «можно сказать, что любые методы, добавленные через эти категории протоколов, явно необязательны». Да, вы могли бы это сделать и теоретически обойти проблему, описанную выше. Но если вы собираетесь это сделать, вы можете просто сделать его категорией в первую очередь, а затем проверить, чтобы class respondsToSelector: перед вызовом метода.

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

    Основное использование для чего-то вроде этого – это когда вы хотите определить категорию, которая зависит от настроенной версии classа. (Представьте, что у меня есть подclassы UIViewController, которые соответствуют протоколу Foo, что означает, что у них есть свойство foo, код моей категории может нуждаться в свойстве foo, но я не могу применить его к протоколу Foo, и если я просто применил его для UIViewController код не будет компилироваться по умолчанию, а принудительное его компиляция означает, что кто-то, выполняющий интроспекцию, или просто зависает, может вызывать ваш код, который зависит от протокола. Гибридный подход может работать следующим образом:

     @protocol Foo - (void)fooMethod @property (retain) NSString *foo; @end @implementation UIViewController (FooCategory) - (void)fooMethod { if (![self conformsToProtocol:@protocol(Foo)]) { return; } UIViewController *me = (UIViewController*) self; // For the rest of the method, use "me" instead of "self" NSLog(@"My foo property is \"%@\"", me.foo); } @end 

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

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

    Это не имеет особого смысла, поскольку протокол не может реально реализовать этот метод. Протокол – это способ объявить, что вы поддерживаете некоторые методы. Добавление метода в этот список за пределами протокола означает, что все «соответствующие» classы случайно объявляют новый метод, даже если они не реализуют его. Если какой-то class реализовал протокол NSObject, но не спустился с NSObject, а затем вы добавили метод к протоколу, который нарушил бы соответствие classа.

    Тем не менее, вы можете создать новый протокол, включающий старый, с объявлением типа @protocol SpecialObject .

    Я думаю, вы можете смешивать условия здесь и там. Расширения, категории, протоколы, интерфейсы и classы – все это в Objective-C. В языке Objective-C 2.0 Apple хорошо описывает различия, включая преимущества и недостатки использования категорий и расширений.

    Если вы думаете об этом, что такое «категория» или «расширение» в концептуальном смысле? Это способ добавить функциональность к classу. В Objective-C протоколы не предназначены для реализации. Поэтому, как бы вы могли добавить или расширить реализацию чего-то, что не имеет реализации для начала?

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

    т.е.

     @interface NSString (MyCategory) - (BOOL) startsWith: (NSString*) prefix; @end @protocol MyExtendedProtocolName  //Method declarations go here @end 

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

     @interface MyClass  

    также, будьте осторожны при подclassификации NSString, это кластер, и вы не всегда можете получить поведение, которое ожидаете.

    Адам Шарп опубликовал решение, которое сработало для меня.

    Он включает в себя 3 этапа:

    1. Определение методов, которые вы хотите добавить как @optional по протоколу.
    2. Создание объектов, которые вы хотите расширить, соответствует этому протоколу.
    3. Копирование этих методов в те объекты во время выполнения.

    Ознакомьтесь со ссылкой для получения полной информации.

    Давайте будем гением компьютера.