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

时间:2022-09-06 20:10:41

In Objective-C, I can add methods to existing classes with a category, e.g.

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

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

Is it also possible to do this with protocols, i.e. if there was a NSString protocol, something like:

是否也可以用协议来实现这一点,例如,如果有一个NSString协议,比如:

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

I want to do this since I have several extensions to NSObject (the class), using only public NSObject methods, and I want those extensions also to work with objects implementing the protocol .

我想这样做,因为我有几个NSObject(类)的扩展,只使用公共的NSObject方法,而且我还想让这些扩展与实现协议的对象一起工作。

To give a further example, what if I want to write a method logDescription that prints an object's description to the log:

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

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

I can of course add this method to NSObject, but there are other classes that do not inherit from NSObject, where I'd also like to have this method, e.g. NSProxy. Since the method only uses public members of protocol , it would be best to add it to the protocol.

我当然可以将这个方法添加到NSObject,但是还有其他类没有从NSObject继承,我也希望有这个方法,例如NSProxy。由于该方法只使用协议的公共成员,因此最好将其添加到协议中。

Edit: Java 8 now has this with "virtual extension methods" in interfaces: http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf. This is exactly what I would like to do in Objective-C. I did not see this question earning this much attention...

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

Regards, Jochen

问候,约

7 个解决方案

#1


17  

extObjC has the NEATEST stuff you can do with Protocols / Categories... first off is @concreteprotocol...

extObjC有最整洁的东西你可以做的协议/类别…首先是@concreteprotocol…

  • Defines a "concrete protocol," which can provide default implementations of methods within protocol.
  • 定义“具体协议”,它可以在协议中提供方法的默认实现。
  • An @protocol block should exist in a header file, and a corresponding @concreteprotocol block in an implementation file.
  • 头文件中应该有一个@protocol块,实现文件中应该有一个对应的@具象协议块。
  • Any object that declares itself to conform to this protocol will receive its method implementations, but only if no method by the same name already exists.
  • 任何声明自身符合此协议的对象都将接收其方法实现,但前提是不存在同名的方法。

MyProtocol.h

MyProtocol.h

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

MyProtocol.m

MyProtocol.m

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

so declaring an object MyDumbObject : NSObject <MyProtocol> will automatically return YES to isConcrete.

因此,声明对象MyDumbObject: NSObject 将自动返回YES给isConcrete。

Also, they have pcategoryinterface(PROTOCOL,CATEGORY) which "defines the interface for a category named CATEGORY on a protocol PROTOCOL". Protocol categories contain methods that are automatically applied to any class that declares itself to conform to PROTOCOL." There is an accompanying macro you also have to use in your implementation file. See the docs.

此外,它们还有pcategoryinterface(协议,类别),它“为协议协议上命名的类别定义接口”。协议类别包含自动适用于声明自己符合协议的任何类的方法。在实现文件中还需要使用一个附带的宏。看文档。

Last, but NOT least / not directly related to @protocols is synthesizeAssociation(CLASS, PROPERTY), which "synthesizes a property for a class using associated objects. This is primarily useful for adding properties to a class within a category. PROPERTY must have been declared with @property in the interface of the specified class (or a category upon it), and must be of object type."

最后,但并非最不重要/不直接与@protocol相关的是synthesizeAssociation(类、属性),它“使用关联对象为类合成属性”。这对于向类别中的类添加属性非常有用。属性必须在指定类(或其上的类别)的接口中使用@property声明,并且必须是对象类型。

So many of the tools in this library open (way-up) the things you can do with ObjC... from multiple inheritance... to well, your imagination is the limit.

这个库中的许多工具都是开放的,你可以用ObjC做些什么……从多重继承…好吧,你的想象力是极限。

#2


25  

Short answer: No.

简短的回答:没有。

Long answer: how would this work? Imagine you could add methods to existing protocols? How would this work? Imagine we wanted to add another method to NSCoding, say -(NSArray *) codingKeys; This method is a required method that returns an array of the keys used to encoding the object.

长话短说:这是怎么回事?假设您可以向现有协议添加方法?这将如何工作?假设我们想要为NSCoding添加另一个方法,比如-(NSArray *) codingKeys;该方法是一个必需的方法,返回用于编码对象的键的数组。

The problem is that there are existing classes (like, say NSString) that already implement NSCoding, but don't implement our codingKeys method. What should happen? How would the pre-compiled framework know what to do when this required message gets sent to a class that does not implement it?

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

You could say "we can add the definition of this method via a category" or "we could say that any methods added via these protocol categories are explicitly optional". Yes, you could do this and theoretically get around the problem I've described above. But if you're going to do that, you might as well just make it a category in the first place, and then check to make sure the class respondsToSelector: before invoking the method.

您可以说“我们可以通过一个类别添加这个方法的定义”或者“我们可以说通过这些协议类别添加的任何方法都是显式可选的”。是的,你可以这样做,理论上解决我上面提到的问题。但是如果您要这样做,您最好首先将它设置为一个类别,然后检查以确保在调用该方法之前的类respondsToSelector:。

#3


22  

While it's true that you can't define categories for protocols (and wouldn't want to, because you don't know anything about the existing object), you can define categories in such a way that the code only applies to an object of the given type that has the desired protocol (sort of like C++'s partial template specialization).

虽然你确实不能为协议定义类别(不想,因为你不知道任何关于现有的对象),您可以定义类的代码只适用于一个对象的特定类型所需的协议(有点像c++的部分模板专门化)。

The main use for something like this is when you wish to define a category that depends on a customized version of a class. (Imagine that I have UIViewController subclasses that conform to the Foo protocol, meaning they have the foo property, my category code may have need of the foo property, but I can't apply it to the Foo protocol, and if I simply apply it to UIViewController, the code won't compile by default, and forcing it to compile means someone doing introspection, or just screwing up, might call your code which depends on the protocol. A hybrid approach could work like this:

这类东西的主要用途是当您希望定义依赖于类的定制版本的类别时。(想象一下我ui子类,符合Foo协议,这意味着他们有Foo财产,我的类别代码可能需要Foo的财产,但是我不能把它应用到Foo协议,如果我只是应用于ui,默认代码不能编译,并迫使它编译意味着某人做反省,还是搞砸了,可能会打电话给你的代码依赖于协议。混合方法可以这样工作:

@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

With the hybrid approach, you can write the code only once (per class that is supposed to implement the protocol) and be sure that it won't affect instances of the class that don't conform to the protocol.

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

The downside is that property synthesis/definition still has to happen in the individual subclasses.

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

#4


7  

It isn't really meaningful to do so since a protocol can't actually implement the method. A protocol is a way of declaring that you support some methods. Adding a method to this list outside the protocol means that all "conforming" classes accidentally declare the new method even though they don't implement it. If some class implemented the NSObject protocol but did not descend from NSObject, and then you added a method to the protocol, that would break the class's conformance.

这样做没有什么意义,因为协议实际上不能实现这个方法。协议是声明您支持某些方法的一种方式。在协议之外添加一个方法意味着所有“一致”类都不小心声明了新方法,即使它们没有实现它。如果某个类实现了NSObject协议,但没有从NSObject派生,然后您向该协议添加了一个方法,这会破坏类的一致性。

You can, however, create a new protocol that includes the old one with a declaration like @protocol SpecialObject <NSObject>.

但是,您可以创建一个新的协议,其中包括一个包含声明的旧协议,如@protocol SpecialObject

#5


0  

I think you may be mixing up terms here and there. Extensions, Categories, Protocols, Interfaces and Classes are all different things in Objective-C. In The Objective-C 2.0 Language Apple describes the differences very well, including the benefits and drawbacks to using categories and extensions.

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

If you think about it, what is a "Category" or "Extension" in the conceptual sense? It's a way of adding functionality to a Class. In Objective-C, protocols are designed to have no implementation. Therefore, how would you add or extend the implementation of something that doesn't have implementation to begin with?

如果你仔细想想,概念上的“范畴”或“扩展”是什么?它是向类添加功能的一种方式。在Objective-C中,协议被设计成没有实现。因此,如何添加或扩展没有实现的实现?

#6


0  

if you're already writing a category, why not just add in the protocol definition in the header right after the category definition?

如果您已经在编写一个类别,为什么不直接在类定义之后的标题中添加协议定义呢?

i.e.

即。

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

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

this way any class that imports the category header will also get the protocol definition, and you can add it into your class..

这样,任何导入类别头的类都将获得协议定义,您可以将其添加到您的类中。

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

also, be careful when subclassing NSString, it's a cluster and you may not always get the behaviour you're expecting.

此外,在子类化NSString时要小心,它是一个集群,您可能不会总是得到预期的行为。

#7


0  

Adam Sharp posted a solution that worked for me.

Adam Sharp发布了一个对我有用的解决方案。

It involves 3 steps:

它包括三个步骤:

  1. Defining the methods you want to add as @optional on a protocol.
  2. 在协议中定义要添加的方法为@optional。
  3. Making the objects you want to extend conform to that protocol.
  4. 使要扩展的对象符合该协议。
  5. Copying those methods into those objects at runtime.
  6. 在运行时将这些方法复制到这些对象中。

Check out the link for the full details.

查看完整的细节链接。

#1


17  

extObjC has the NEATEST stuff you can do with Protocols / Categories... first off is @concreteprotocol...

extObjC有最整洁的东西你可以做的协议/类别…首先是@concreteprotocol…

  • Defines a "concrete protocol," which can provide default implementations of methods within protocol.
  • 定义“具体协议”,它可以在协议中提供方法的默认实现。
  • An @protocol block should exist in a header file, and a corresponding @concreteprotocol block in an implementation file.
  • 头文件中应该有一个@protocol块,实现文件中应该有一个对应的@具象协议块。
  • Any object that declares itself to conform to this protocol will receive its method implementations, but only if no method by the same name already exists.
  • 任何声明自身符合此协议的对象都将接收其方法实现,但前提是不存在同名的方法。

MyProtocol.h

MyProtocol.h

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

MyProtocol.m

MyProtocol.m

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

so declaring an object MyDumbObject : NSObject <MyProtocol> will automatically return YES to isConcrete.

因此,声明对象MyDumbObject: NSObject 将自动返回YES给isConcrete。

Also, they have pcategoryinterface(PROTOCOL,CATEGORY) which "defines the interface for a category named CATEGORY on a protocol PROTOCOL". Protocol categories contain methods that are automatically applied to any class that declares itself to conform to PROTOCOL." There is an accompanying macro you also have to use in your implementation file. See the docs.

此外,它们还有pcategoryinterface(协议,类别),它“为协议协议上命名的类别定义接口”。协议类别包含自动适用于声明自己符合协议的任何类的方法。在实现文件中还需要使用一个附带的宏。看文档。

Last, but NOT least / not directly related to @protocols is synthesizeAssociation(CLASS, PROPERTY), which "synthesizes a property for a class using associated objects. This is primarily useful for adding properties to a class within a category. PROPERTY must have been declared with @property in the interface of the specified class (or a category upon it), and must be of object type."

最后,但并非最不重要/不直接与@protocol相关的是synthesizeAssociation(类、属性),它“使用关联对象为类合成属性”。这对于向类别中的类添加属性非常有用。属性必须在指定类(或其上的类别)的接口中使用@property声明,并且必须是对象类型。

So many of the tools in this library open (way-up) the things you can do with ObjC... from multiple inheritance... to well, your imagination is the limit.

这个库中的许多工具都是开放的,你可以用ObjC做些什么……从多重继承…好吧,你的想象力是极限。

#2


25  

Short answer: No.

简短的回答:没有。

Long answer: how would this work? Imagine you could add methods to existing protocols? How would this work? Imagine we wanted to add another method to NSCoding, say -(NSArray *) codingKeys; This method is a required method that returns an array of the keys used to encoding the object.

长话短说:这是怎么回事?假设您可以向现有协议添加方法?这将如何工作?假设我们想要为NSCoding添加另一个方法,比如-(NSArray *) codingKeys;该方法是一个必需的方法,返回用于编码对象的键的数组。

The problem is that there are existing classes (like, say NSString) that already implement NSCoding, but don't implement our codingKeys method. What should happen? How would the pre-compiled framework know what to do when this required message gets sent to a class that does not implement it?

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

You could say "we can add the definition of this method via a category" or "we could say that any methods added via these protocol categories are explicitly optional". Yes, you could do this and theoretically get around the problem I've described above. But if you're going to do that, you might as well just make it a category in the first place, and then check to make sure the class respondsToSelector: before invoking the method.

您可以说“我们可以通过一个类别添加这个方法的定义”或者“我们可以说通过这些协议类别添加的任何方法都是显式可选的”。是的,你可以这样做,理论上解决我上面提到的问题。但是如果您要这样做,您最好首先将它设置为一个类别,然后检查以确保在调用该方法之前的类respondsToSelector:。

#3


22  

While it's true that you can't define categories for protocols (and wouldn't want to, because you don't know anything about the existing object), you can define categories in such a way that the code only applies to an object of the given type that has the desired protocol (sort of like C++'s partial template specialization).

虽然你确实不能为协议定义类别(不想,因为你不知道任何关于现有的对象),您可以定义类的代码只适用于一个对象的特定类型所需的协议(有点像c++的部分模板专门化)。

The main use for something like this is when you wish to define a category that depends on a customized version of a class. (Imagine that I have UIViewController subclasses that conform to the Foo protocol, meaning they have the foo property, my category code may have need of the foo property, but I can't apply it to the Foo protocol, and if I simply apply it to UIViewController, the code won't compile by default, and forcing it to compile means someone doing introspection, or just screwing up, might call your code which depends on the protocol. A hybrid approach could work like this:

这类东西的主要用途是当您希望定义依赖于类的定制版本的类别时。(想象一下我ui子类,符合Foo协议,这意味着他们有Foo财产,我的类别代码可能需要Foo的财产,但是我不能把它应用到Foo协议,如果我只是应用于ui,默认代码不能编译,并迫使它编译意味着某人做反省,还是搞砸了,可能会打电话给你的代码依赖于协议。混合方法可以这样工作:

@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

With the hybrid approach, you can write the code only once (per class that is supposed to implement the protocol) and be sure that it won't affect instances of the class that don't conform to the protocol.

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

The downside is that property synthesis/definition still has to happen in the individual subclasses.

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

#4


7  

It isn't really meaningful to do so since a protocol can't actually implement the method. A protocol is a way of declaring that you support some methods. Adding a method to this list outside the protocol means that all "conforming" classes accidentally declare the new method even though they don't implement it. If some class implemented the NSObject protocol but did not descend from NSObject, and then you added a method to the protocol, that would break the class's conformance.

这样做没有什么意义,因为协议实际上不能实现这个方法。协议是声明您支持某些方法的一种方式。在协议之外添加一个方法意味着所有“一致”类都不小心声明了新方法,即使它们没有实现它。如果某个类实现了NSObject协议,但没有从NSObject派生,然后您向该协议添加了一个方法,这会破坏类的一致性。

You can, however, create a new protocol that includes the old one with a declaration like @protocol SpecialObject <NSObject>.

但是,您可以创建一个新的协议,其中包括一个包含声明的旧协议,如@protocol SpecialObject

#5


0  

I think you may be mixing up terms here and there. Extensions, Categories, Protocols, Interfaces and Classes are all different things in Objective-C. In The Objective-C 2.0 Language Apple describes the differences very well, including the benefits and drawbacks to using categories and extensions.

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

If you think about it, what is a "Category" or "Extension" in the conceptual sense? It's a way of adding functionality to a Class. In Objective-C, protocols are designed to have no implementation. Therefore, how would you add or extend the implementation of something that doesn't have implementation to begin with?

如果你仔细想想,概念上的“范畴”或“扩展”是什么?它是向类添加功能的一种方式。在Objective-C中,协议被设计成没有实现。因此,如何添加或扩展没有实现的实现?

#6


0  

if you're already writing a category, why not just add in the protocol definition in the header right after the category definition?

如果您已经在编写一个类别,为什么不直接在类定义之后的标题中添加协议定义呢?

i.e.

即。

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

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

this way any class that imports the category header will also get the protocol definition, and you can add it into your class..

这样,任何导入类别头的类都将获得协议定义,您可以将其添加到您的类中。

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

also, be careful when subclassing NSString, it's a cluster and you may not always get the behaviour you're expecting.

此外,在子类化NSString时要小心,它是一个集群,您可能不会总是得到预期的行为。

#7


0  

Adam Sharp posted a solution that worked for me.

Adam Sharp发布了一个对我有用的解决方案。

It involves 3 steps:

它包括三个步骤:

  1. Defining the methods you want to add as @optional on a protocol.
  2. 在协议中定义要添加的方法为@optional。
  3. Making the objects you want to extend conform to that protocol.
  4. 使要扩展的对象符合该协议。
  5. Copying those methods into those objects at runtime.
  6. 在运行时将这些方法复制到这些对象中。

Check out the link for the full details.

查看完整的细节链接。