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

StackOverflow https://stackoverflow.com/questions/1521267

Вопрос

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

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

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

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

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

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

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

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

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

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

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

Решение

В extObjC самые АККУРАТНЫЕ материалы вы можете сделать это с помощью Протоколов / Категорий...во-первых, это @concreteprotocol...

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

MyProtocol.h

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

Мой протокол.m

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

итак, объявляем объект MyDumbObject : NSObject <MyProtocol> автоматически вернется YES Для isConcrete.

Кроме того, у них есть pcategoryinterface(PROTOCOL,CATEGORY) который "определяет интерфейс для категории с именем CATEGORY в протоколе PROTOCOL".Категории протоколов содержат методы, которые автоматически применяются к любому классу, который объявляет себя соответствующим ПРОТОКОЛУ ". В вашем файле реализации также есть сопутствующий макрос, который вы должны использовать.Посмотрите документы.

Последнее, но НЕ менее важное / не непосредственно связанный с @protocols является synthesizeAssociation(CLASS, PROPERTY), который "синтезирует свойство для класса с использованием связанных объектов.Это в первую очередь полезно для добавления свойств к классу внутри категории.ИМУЩЕСТВО должно быть задекларировано с @property в интерфейсе указанного класса (или соответствующей ему категории) и должен быть объектного типа ".

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

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

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

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

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

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

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

Основное применение чего-то подобного - это когда вы хотите определить категорию, которая зависит от настроенной версии класса.(Представьте, что у меня есть подклассы 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<Foo> *me = (UIViewController<Foo>*) self;
    // For the rest of the method, use "me" instead of "self"
    NSLog(@"My foo property is \"%@\"", me.foo);
}
@end

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

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

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

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

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

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

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

т. е.

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

@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end

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

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

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

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

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

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

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

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