Подтвердить что ты не робот

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

В 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. Я не видел, чтобы этот вопрос привлек это внимание...

С уважением, Jochen

4b9b3361

Ответ 1

extObjC имеет НЕОБХОДИМЫЙ материал, который вы можете делать с протоколами/категориями... 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 <MyProtocol> автоматически вернет YES в isConcrete.

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Основное использование для чего-то вроде этого - это когда вы хотите определить категорию, которая зависит от настроенной версии класса. (Представьте, что у меня есть подклассы 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

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

то есть.

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

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

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

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

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

Ответ 7

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

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

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

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