在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中做的事情。我没有看到这个问题引起如此多的关注......
此致 约亨
答案 0 :(得分:25)
简短回答:不。
答案很长:这怎么样?想象一下,可以向现有协议添加方法吗?这怎么样?想象一下,我们想要为NSCoding添加另一种方法,比如说-(NSArray *) codingKeys;
这种方法是一种必需的方法,它返回用于编码对象的密钥数组。
问题是现有的类(比如NSString)已经实现了NSCoding,但是没有实现我们的codingKeys
方法。应该怎么办?当 required 消息被发送到没有实现它的类时,预编译框架将如何知道该怎么做?
您可以说“我们可以通过类别添加此方法的定义”或“我们可以说通过这些协议类别添加的任何方法都是明确可选的”。是的,你可以这样做,理论上可以解决我上面描述的问题。但是,如果您要这样做,您可能只是首先将其设为一个类别,然后在调用该方法之前检查以确保类respondsToSelector:
。
答案 1 :(得分: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
使用混合方法,您只能编写一次代码(每个应该实现协议的类),并确保它不会影响不符合协议的类的实例。
缺点是属性合成/定义仍然必须在各个子类中发生。
答案 2 :(得分:18)
extObjC has the NEATEST stuff您可以使用协议/类别...首先是@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)
“定义协议PROTOCOL上名为CATEGORY的类别的接口”。协议类别包含自动应用于声明自身符合PROTOCOL的任何类的方法。“您还必须在实现文件中使用随附的宏。请参阅文档。
最后,但与<{1}}相关的直接 @protocols
,“使用关联对象合成类的属性。这主要用于向类别中的类添加属性。必须在指定类的接口中使用synthesizeAssociation(CLASS, PROPERTY)
声明PROPERTY (或其上的类别),并且必须是对象类型。“
这个库中的这么多工具打开(整理)你可以用ObjC做的事情...来自多重继承...好吧,你的想象力是极限。 / p>
答案 3 :(得分:7)
这样做并没有多大意义,因为协议实际上无法实现该方法。协议是一种声明支持某些方法的方法。在协议之外向此列表添加方法意味着所有“符合”类都会意外地声明新方法,即使它们没有实现它。如果某个类实现了NSObject协议但没有从NSObject继承,那么你在协议中添加了一个方法,这会破坏类的一致性。
但是,您可以创建一个新协议,其中包含旧协议,声明为@protocol SpecialObject <NSObject>
。
答案 4 :(得分:0)
我认为你可能会在这里和那里混淆术语。扩展,类别,协议,接口和类在Objective-C中都是不同的东西。在The Objective-C 2.0 Language Apple中很好地描述了差异,包括使用类别和扩展的好处和缺点。
如果你考虑一下,概念意义上的“类别”或“扩展”是什么?这是一种为类添加功能的方法。在Objective-C中,协议被设计为没有实现。因此,您将如何添加或扩展没有实现的东西的实现?
答案 5 :(得分:0)
如果您已经在编写类别,为什么不在类别定义之后立即在标题中添加协议定义?
即
@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end
@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end
这样导入类别标题的任何类也将获得协议定义,您可以将其添加到类中。
@interface MyClass <OriginalProtocol,MyExtendedProtocolName>
另外,在子类化NSString时要小心,它是一个集群,你可能并不总是得到你期望的行为。
答案 6 :(得分:0)
Adam Sharp posted a solution为我工作。
它涉及3个步骤:
@optional
的方法。查看链接以获取完整详细信息。