在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,但也有其他类不从NSObjectinheritance,我也想要这种方法,例如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; 

MyProtocol.m

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

所以声明一个对象MyDumbObject : NSObject <MyProtocol>会自动返回YESisConcrete

另外,它们具有pcategoryinterface(PROTOCOL,CATEGORY) ,它定义了协议PROTOCOL中名为CATEGORY的类别的接口。 协议类别包含自动应用于任何声明自己符合PROTOCOL的类的方法。“在实现文件中还有一个伴随的macros,请参阅文档。

最后,与@protocols并不是直接相关的是synthesizeAssociation(CLASS, PROPERTY) ,它“为一个类使用关联的对象合成一个属性,主要用于在属性类中添加属性。在指定的类(或其类别)的接口中使用@property声明,并且必须是对象types。

所以这个库中的许多工具都可以打开ObjC可以做的事情……从多重inheritance到好的你的想象力是有限的。

简短的回答:不。

长答案:这将如何工作? 想象一下,你可以添加方法到现有的协议? 这将如何工作? 想象一下,我们想要添加另一种方法来NSCoding,说-(NSArray *) codingKeys; 此方法是返回用于编码对象的键的数组的必需方法。

问题是有已经实现了NSCoding的类(比如说NSString),但是没有实现我们的codingKeys方法。 应该发生什么? 预编译的框架如何知道在这个需要的消息被发送给一个没有实现它的类时要做什么?

你可以说“我们可以通过一个类别添加这个方法的定义”,或者“我们可以说通过这些协议类别添加的任何方法都是明确可选的”。 是的,你可以做到这一点,理论上解决我上面描述的问题。 但是如果你打算这么做的话,那么你可以把它放在第一位,然后在调用方法之前检查确保类是respondsToSelector:

虽然确实无法为协议定义类别(并且不想要,因为对现有对象不了解任何内容),但您可以定义类别,以使代码仅适用于具有所需协议的给定types(有点像C ++的部分模板特化)。

这样的主要用途是当你想要定义一个依赖于一个类的定制版本的类别。 (想象一下,我有符合Foo协议的UIViewController子类,这意味着它们具有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中下来,然后你在协议中添加了一个方法,这将会破坏类的一致性。

但是,您可以创build一个新的协议,其中包含旧协议,并声明像@protocol SpecialObject <NSObject>这样的声明。

我想你可能会混淆在这里和那里的条款。 Objective-C中的扩展,类别,协议,接口和类都是不同的东西。 在Objective-C 2.0语言中, Apple很好地描述了这些差异,包括使用类别和扩展的优点和缺点。

如果你仔细想想,概念意义上的“范畴”或“扩展”是什么? 这是向类添加function的一种方式。 在Objective-C中,协议被devise为没有实现。 因此,如何添加或扩展一些没有实现的东西的实现呢?

如果你已经写了一个类别,为什么不直接在类别定义之后的头部添加协议定义呢?

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

这样任何导入类别标题的类都将获得协议定义,并且可以将它添加到你的类中。

 @interface MyClass <OriginalProtocol,MyExtendedProtocolName> 

另外,在inheritanceNSString的时候要小心,它是一个集群,你可能并不总是得到你期望的行为。

Adam Sharp 发布了一个适合我的解决scheme 。

它涉及3个步骤:

  1. 定义你想在协议上添加为@optional的方法。
  2. 使您想要扩展的对象符合该协议。
  3. 在运行时将这些方法复制到这些对象中。

查看完整的细节链接。