Defining categories for protocols in Objective-C?

23,568

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.

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?

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.

Share:
23,568
Jochen
Author by

Jochen

Updated on December 05, 2020

Comments

  • Jochen
    Jochen over 3 years

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

    @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:

    @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 .

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

    - (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.

    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...

    Regards, Jochen

  • Jochen
    Jochen over 14 years
    I think in theory this could work. Since Objective-C allows adding methods to existing classes with class_addMethod, I could get all defined classes, check if they implement a specific protocol and then add my method to the class (since it only uses the protocol's methods, it does not depend on the class). The question came up because we wanted to define an "extended protocol method" once, and not for every class, and have the compiler know about this, so it doesn't have to throw warnings. So even if we can do this with the runtime, the compiler warnings still are there.
  • Filip Kunc
    Filip Kunc about 14 years
    I think that in reality it can work too. C# 3.0 allows to extend interfaces via static classes. It is different mechanism than categories, because it is only syntatic sugar. In Objective-C category really adds methods to class.
  • hatfinch
    hatfinch about 14 years
    Here's why it would be useful to add methods to existing protocols: Let's say you have a category on NSObject defining -foo, and a category on UIApplication defining -bar. In -bar, you want to call [self.delegate foo]. UIApplicationDelegate conforms to NSObject. How do you convince the compiler that calling -foo on self.delegate is fine?
  • Dave DeLong
    Dave DeLong about 14 years
    @hatfinch red herring. You wouldn't put the method on <NSObject> (the protocol), you'd put it on NSObject (the class). However, I think the entire premise is wrong. Generally, if you find you have to access the application instance to achieve proper functionality, then you probably need to rethink your app's architecture.
  • Dave DeLong
    Dave DeLong about 14 years
    @hatfinch the <NSObject> protocol exists so that you can declare variables as id<SomeProtocol> someVar; and not get a warning when you try to [someVar retain];. <SomeProtocol> would be declared as @protocol SomeProtocol <NSObject>, thereby giving any <SomeObject> the standard retain, release, respondsToSelector:, etc methods.
  • hatfinch
    hatfinch about 14 years
    I just used UIApplicationDelegate as an example; the point stands for any delegate. I understand the purpose of <NSObject>, I just think it would be useful to be able to extend it (in particular, or any protocol in general). For instance, it would be really useful if -performSelectorOnMainThread:... etc. were in <NSObject>.
  • executor21
    executor21 about 14 years
    Here's a place where this feature would be useful: I have a field in a class of type id<MyProtocol>, where MyProtocol includes <NSObject>. I've used a category to add a method to NSObject, and want to call that method on this field, but the compiler produces a warning. I wish my category method could be added to <NSObject>.
  • Dave DeLong
    Dave DeLong about 14 years
    @Executor21 so instead of declaring it as id<MyProtocol> foo, declare it as NSObject<MyProtocol> *foo.
  • Jochen
    Jochen almost 14 years
    NSObject<MyProtocol> only works if it's an NSObject. What if it's e.g. a NSProxy. I added a sample for this.
  • Dave DeLong
    Dave DeLong almost 14 years
    @Jochen it's possible that there are extreme edge cases where this might be useful, but in the years that I've been doing Cocoa development, I've never come across a situation where I needed this and couldn't work around it.
  • user102008
    user102008 almost 13 years
    @Jochen: "What if it's e.g. a NSProxy" So what if it's an NSProxy? Are you going to add an implementation for NSProxy too?
  • user102008
    user102008 almost 13 years
    categories also serve to declare the method in the class (e.g. informal protocols) so that the compiler doesn't warn that the object doesn't support the method, even if you may or may not actually implement it
  • Jeremy W. Sherman
    Jeremy W. Sherman almost 13 years
    @Jochen: The declared object type is simply to assist in looking up the correct argument type. You can tell the compiler an object is whatever type you want; the truth of that assertion isn't tested till runtime. Even if you know it's an NSProxy, lie to the compiler really sweetly that it's an NSObject<MyProtocol>, and the compiler will believe you and act accordingly.
  • jasongregori
    jasongregori over 12 years
    Tell me if this case is stupid. I want to make a category on the protocol NSFastEnumeration so I can have a map method on dictionaries and arrays. Obviously, I can work around this but it would be really nice. I would know that I could use any methods that the protocol declares.
  • jasongregori
    jasongregori over 12 years
    I reread your answer and I guess I could just put a category on NSObject and double check the object follows the protocol before running my map method. Not as nice since it will show up for all objects but an okay work around. Thanks!
  • aLevelOfIndirection
    aLevelOfIndirection about 11 years
    Sorry for being so late to this debate but I, like @jasongregory, wanted to do exactly the same thing. Add a bunch of map methods to NSFastEnumerating. There could be multitudes of good reasons to implement a category on a protocol and it would work fine if the category only depended on methods already defined in the protocol. In fact, it may not be possible with the objective-c language constructs but it sould be possible with the runtime since MacRuby can do something similar with mixins and MacRuby is implemented on top of the objective-c runtime.
  • Senseful
    Senseful almost 11 years
    Adam Sharp posted a solution that works pretty well check it out here.
  • Dan Rosenstark
    Dan Rosenstark over 8 years
    And then there was Swift 2.0 and nobody else asked whether categories of a protocol are a good idea ever again.
  • Slipp D. Thompson
    Slipp D. Thompson almost 8 years
    This answer tries to dismiss the question by raising a number of implementation unknowns (in the answerer's mind). In reality, these “unknowns” have been answered & implemented before, and can be done again. At the time of answer-writing Ruby is a good place to look for a robust message-resolution chain that handles category methods (module methods in Ruby terms), and in the present Swift is the go-to implementation in the Apple-verse as others have noted. I believe Smalltalk also holds some of the answers to the questions frivolously raised in this answer.
  • Benjohn
    Benjohn almost 7 years
    The answer's arguments against this are specious. As Swift, other languages, and extObjC demonstrate, there are sound and predictable semantics for the OPs request. The extension would require an implementation using existing methods of the protocol. As a further development, conformers to the protocol could override the default implementation. Vanilla ObjC doesn't do this to the best of my knowledge and I would avoid using a third party library that introduced it for production code.
  • erikprice
    erikprice over 6 years
    Aren't you declaring a protocol that extends from a concrete type?