在Objective-C中定义协议的类别?

Joc*_*hen 38 protocols objective-c categories

在Objective-C中,我可以向具有类别的现有类添加方法,例如

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
Run Code Online (Sandbox Code Playgroud)

是否也可以使用协议来执行此操作,即如果存在NSString协议,则类似于:

@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
Run Code Online (Sandbox Code Playgroud)

我想这样做,因为我对NSObject(该类)有几个扩展,只使用公共NSObject方法,我希望这些扩展也能用于实现协议的对象.

再举一个例子,如果我想编写一个方法logDescription,将对象的描述打印到日志中,该怎么办:

- (void) logDescription {
    NSLog(@"%@", [self description]);
}
Run Code Online (Sandbox Code Playgroud)

我当然可以将此方法添加到NSObject,但是还有其他类不从NSObject继承,我也想要使用此方法,例如NSProxy.由于该方法仅使用协议的公共成员,因此最好将其添加到协议中.

编辑:Java 8现在在接口中使用"虚拟扩展方法":http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf.这正是我想在Objective-C中做的事情.我没有看到这个问题引起如此多的关注......

此致,Jochen

Dav*_*ong 25

简答:不.

答案很长:这将如何运作?想象一下,您可以在现有协议中添加方法吗?这怎么样?想象一下,我们想为NSCoding添加另一个方法,比如说-(NSArray *) codingKeys; 这个方法是一个必需的方法,它返回一个用于编码对象的键数组.

问题是现有的类(比如NSString)已经实现了NSCoding,但没有实现我们的codingKeys方法.应该怎么办?当这个必需的消息被发送到没有实现它的类时,预编译的框架将如何知道该怎么做?

您可以说"我们可以通过类别添加此方法的定义"或"我们可以说通过这些协议类别添加的任何方法都是明确可选的".是的,你可以这样做,理论上可以解决我上面描述的问题.但是,如果您要这样做,您可能只是首先将其设为类别,然后respondsToSelector:在调用该方法之前检查以确保该类.

  • 这就是为什么在现有协议中添加方法会有用的原因:假设你有一个定义`-foo`的`NSObject`的类别,以及定义`-bar`的`UIApplication`的类别.在`-bar`中,你想调用`[self.delegate foo]`.`UIApplicationDelegate`符合`NSObject`.你如何说服编译器在`self.delegate`上调用`-foo`是好的? (4认同)
  • 很抱歉这次辩论已经很晚了,但我和@jasongregory一样,想做同样的事情.向NSFastEnumerating添加一堆映射方法.在协议上实现类别可能有很多充分的理由,如果类别仅依赖于协议中已定义的方法,则可以正常工作.实际上,使用objective-c语言结构可能无法实现,但是由于MacRuby可以使用mixins执行类似操作,而MacRuby可以在objective-c运行时之上实现,因此可以使用运行时. (4认同)
  • 然后是Swift 2.0,没有人再问过协议的类别是否是一个好主意. (3认同)

Dou*_*yle 22

虽然你无法定义协议的类别(并且不想,因为你对现有对象一无所知),但你可以用这样的方式定义类别,即代码只适用于具有所需协议的给定类型(类似于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
Run Code Online (Sandbox Code Playgroud)

使用混合方法,您只能编写一次代码(每个应该实现协议的类),并确保它不会影响不符合协议的类的实例.

缺点是属性合成/定义仍然必须在各个子类中发生.


Ale*_*ray 18

extObjC有你可以用Protocols/Categories做的最好的东西 ...首先是@concreteprotocol......

  • 定义"具体协议",它可以提供协议内方法的默认实现.
  • 一个@protocol块应该存在于一个头文件和相应的@concreteprotocol在一个实现文件块.
  • 声明自己符合此协议的任何对象都将接收其方法实现,但前提是不存在同名的方法.

MyProtocol.h

@protocol MyProtocol 
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;   
Run Code Online (Sandbox Code Playgroud)

MyProtocol.m

 @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...
Run Code Online (Sandbox Code Playgroud)

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

此外,它们还pcategoryinterface(PROTOCOL,CATEGORY)"定义了协议PROTOCOL上名为CATEGORY的类别的接口".协议类别包含自动应用于声明自身符合PROTOCOL的任何类的方法."还有一个伴随的宏,您还必须在实现文件中使用.请参阅文档.

最后,但最重要/不直接相关的@protocolssynthesizeAssociation(CLASS, PROPERTY),"使用关联对象合成类的属性.这主要用于向类别中的类添加属性.必须@property在指定的接口中声明PROPERTY class(或其上的类别),并且必须是对象类型."

这个库中的许多工具打开(整理)你可以用ObjC做的事情...来自多重继承......好吧,你的想象力是极限.

  • @HaiFengKao我分叉了曾经被称为"实验"的分支.看看我的主叉,或者查看`EXT`的历史/备用分支,了解您需要的特定功能,这些功能很可能已从主线仓库中删除. (2认同)

Chu*_*uck 7

这样做并没有多大意义,因为协议实际上无法实现该方法.协议是一种声明支持某些方法的方法.在协议之外向此列表添加方法意味着所有"符合"类都会意外地声明新方法,即使它们没有实现它.如果某个类实现了NSObject协议但没有从NSObject继承,那么你在协议中添加了一个方法,这将破坏类的一致性.

但是,您可以创建一个新协议,其中包含旧协议,其声明类似于@protocol SpecialObject <NSObject>.

  • 我认为OP正在尝试做的是为该方法提供(额外)实现.即将方法(带有实现)添加到实现协议的所有对象.这与使用新方法定义扩展协议本身有所不同,当然,由于您提到的原因,这些定义无法实现. (2认同)