Can a category simultaneously implement a protocol? - objective-c

If a category I'm creating for a class adds methods that also fulfill the contract set out by a protocol, I'd like to flag that category class as implementing the protocol, and thereby indicate to the Obj-C pre-processor that the class effectively implements the protocol as well.
Example delegate (for clarity, thanks Ole!):
#protocol SomeDelegate <NSObject>
- (void)someDelegateMessage;
#end
Example category:
#interface NSObject (SomeCategory) <SomeDelegate>
- (void)someDelegateMessage;
#end
And with an otherwise typical implementation
#implement NSObject (SomeCategory)
- (void)someDelegateMessage {}
#end
When I actually try this, I get a warning for each NSObject method:
warning: incomplete implementation of category 'SomeCategory'
warning: method definition for '-description' not found
...
warning: method definition for '-isEqual:' not found
warning: category 'SomeCategory' does not fully implement the 'NSObject' protocol
Works fine if I remove <SomeDelegate> from the declaration, but of course NSObject isn't recognized as a SomeDelegate

A workaround is to declare the protocol on a category with no implementation, and implement the method in a different category, e.g.:
#interface NSObject (SomeCategory) <SomeDelegate>
- (void)someDelegateMessage;
#end
#implementation NSObject (SomeCategory_Impl)
- (void)someDelegateMessage {}
#end
If you do this, NSObject will be considered to conform to <SomeDelegate> at compile time, and runtime checks for someDelegateMessage will succeed. However, conformsToProtocol: runtime checks will fail.
Of course, you should file a bug requesting that methods declared on the core class don’t generate warnings.

Any chance your protocol declaration includes the NSObject protocol? Like this:
#protocol SomeDelegate <NSObject>
...
That's where the warnings are coming from because now your category does not implement the full protocol. In the test code I just typed up, removing NSObject from the protocol removes the compiler warnings.

If you want the compiler to shut up about sending <NSObject> messages (and its important that you remember that thats the protocol name, not the class name) then just use 'id' variables, not 'id' since thats you explicitly telling the compiler "This is an object which only implements the SomeDelegate protocol".
Alternately, use NSObject as your variable type instead.

Related

Protocol methods depending on each other

I have run into this weird situation where I have two protocols and both have methods with parameters that must conform to the other protocol. Sounds confusing, so heres some code:
#protocol ProtocolB <NSObject>
#required
-(void)methodB:(id<ProtocolA>)parameter;
#end
#protocol ProtocolA <NSObject>
#required
-(void)methodA:(id<ProtocolB>)parameter;
#end
The compiler says: "Cannot find protocol declaration for ...". Depending on which protocol comes first, it's either ProtocolA or ProtocolB.
Putting them in different files didn't seem to solve this problem.
Any ideas how I can do this without a (major and possibly complicated) redesign?
Forward declaration of protocol might do. Add #protocol ProtocolA; before ProtocolB

Implement protocol through methods declared in superclass?

I'm wondering if it is possible, in a certain subclass, to "recognise" methods declared in it's superclass as implementations of methods declared in a protocol the subclass adheres to, given that they have the same signatures.
It kind of gets hard to even formulate this, that's why any searches I made turned out fruitless so far.
But let me make the case with an example for better understanding.
Protocol:
#protocol TheProtocol <NSObject>
- (void)theProtocolMethod;
#end
Superclass:
// Superclass does not adhere to TheProtocol
#interface TheSuperClass : NSObject
- (void)theProtocolMethod;
#end
#implementation TheSuperClass
- (void)theProtocolMethod
{
// stuff
}
#end
Subclass:
// SubClass adheres to TheProtocol but does not implement it's methods
// since they are implemented in the class it is subclassing. Is this OK?
#interface TheSubClass : TheSuperClass <TheProtocol>
#end
#implementation TheSubClass
#end
Is this anywhere close to being "OK"?
What about the case TheSubClass was in fact a category of TheSuperClass (declaring adherence to TheProtocol) and not a subclass?
A bit more context:
It's a scenario involving Core Data. I need to publish an accessor defined in an Entity Class, in a Protocol that will be used in a separate Framework for developing plugins for my app. The Protocol itself is fully implemented by a Category of the Core Data Entity Class, except for said accessor which is implemented in the Entity Class itself, hence my question.
In absolute terms, this is perfectly legal. Calling -theProtocolMethod on an instance of TheSubClass would indeed invoke TheSuperClass implementation if TheSubClass itself doesn't implement it. You could even call [super theProtocolMethod] in your subclass implementation, if you wanted.
If TheSubClass was a category on TheSuperClass, the superclass implementation would still be called. However, implementing -theProtocolMethod in the category would replace the super class implementation, so you have to be careful here.
Subjectively, in code-design terms, it's maybe a little odd. You essentially have two separate declarations of the same method to manage, which could potentially cause problems if you tried to refactor. I'm guessing the superclass in your case is a library class that you cannot change. Otherwise, I can't see why TheSuperClass shouldn't just conform to the protocol, rather than declare the method separately.
In theory you're saying the superclass is already compliant with the protocol.
If the compiler complains, you can implement wrapper methods in your subclass that simply call super and return any return value from the call to super.

Category on NSObject implementing protocol causes Unimplemented Method warnings

If I define a subprotocol of the NSObject protocol with extra methods, then define and implement a category on NSObject that declares conformance to that protocol, I get warnings on compilation. The compiler complains that my NSObject category doesn't implement all the methods declared in the NSObject protocol.
I don't understand why this is the case. The NSObject class (in <Foundation/NSObject.h> declares that it conforms to the NSObject protocol (and, redundantly, that it implements these methods) - shouldn't that suffice?
What's the cause of the problem here?
Sample code - if you compile this, you'll see warnings on the NSObject (CategoryToImplementMyProtocol) implementation:
#protocol MyProtocol <NSObject>
- (void)myMethod;
#end
#interface NSObject (CategoryToImplementMyProtocol) <MyProtocol>
#end
#implementation NSObject (CategoryToImplementMyProtocol)
- (void)myMethod
{
NSLog("A la peanut butter sandwiches!");
}
#end
I found a couple of questions addressing this, but all the answers were of the "do this workaround!" variety, not that "this is the cause of the problem" variety (or just plain wrong, despite being accepted...). I know I can work around this. I'd really like to understand why it fails.
The problem is that you are declaring adoption of the <NSObject> protocol again. The way protocols work in Objective-C (for better or for worse) is that a category implementation must implement all of the protocols that are specified in its interface.
In addition, protocol conformance is nominal rather than structural in Objective-C. That is to say, for a class to respond to the correct methods is not enough for it to have adopted that protocol.
Edit: Seems the example code has been changed, and I guess my answer isn't really relevant anymore.

Protocol inheritance in Objective C

I've got a project which has in it a protocol, a class implementing that protocol, and a subclass of the implementation class. This is our production application.
#protocol ProductionProtocol<NSObject>
#property (nonatomic, retain) NSString *role;
#end
#interface BaseProduction : NSObject<ProductionProtocol>
NSString *role;
#end
#implementation BaseProduction
#synthesize role;
#end
#interface Production : BaseProduction
#end
#implementation Production
#end
I've also got a proof of concept (POC) application, which is implemented as a separate project that includes the production application. In the POC application, I have a protocol that extends the production protocol, and a class that extends the production class.
#protocol POCProtocol<ProductionProtocol>
-(void)cancel;
#end
#interface POC : Production<POCProtocol>
#end
#implementation POC
-(void)cancel{...}
#end
Notice that in the ProductionProtocol, I've got a role NSString which is declared, and implemented in the BaseProduction interface/class. in the POC, I've got a method 'cancel' which is declared in the protocol, but not in the interface/class.
So here's my question: with my class structure set up like this, I get this warning:
Property 'role' requires method '-role' to be defined - use #synthesize, #dynamic or provide a method implementation
I don't understand why I'm getting this warning. Since the synthesized properties are in the base class, they should be available to the POC class - and a quick test seems to confirm that they are. So what am I doing wrong here?
There is no official language definition for Objective-C, but according to Apple:
When a class adopts a protocol, it must implement the required methods the protocol declares, as mentioned earlier. In addition, it must conform to any protocols the adopted protocol incorporates. If an incorporated protocol incorporates still other protocols, the class must also conform to them. A class can conform to an incorporated protocol using either of these techniques:
Implementing the methods the protocol declares
Inheriting from a class that adopts the protocol and implements the methods
However, reports are that GCC doesn't recognize that the property is in an incorporated protocol and is implemented in a superclass. You could change your compiler to Clang, which is reported to handle this in the specified manner, or you could just use #dynamic to tell the compiler that an implementation of the property will be provided at run time (in this case, by inheritance from the superclass).
[XCode 3.2]
The compiler bug is in implementation of protocol inheritance. In the example when compiling POC there are two paths to ProductionProtocol:
POC -> Production -> BaseProduction -> ProductionProtocol
POC -> POCProtocol -> ProductionProtocol
This confuses GCC 4.2, it doesn't confuse Clang (LLVM 1.6, XCode 3.2).
If you change POCProtocol to:
#protocol POCProtocol//<ProductionProtocol>
the error will go away.
You could just comment out the protocol-to-protocol inheritance and leave a TODO to remove when the compiler is fixed.
Looks like a classic compiler writer bug to me, or maybe someone confused with .NET semantics (where you can re-implement interfaces (protocols in Obj-C) along the inheritance chain).
#danyowdee suggests you file a bug report, though as it is not in Clang (I didn't fire up my mothballed XCode 4 to test its compilers) I suspect this is unlikely to be a high priority to get fixed...

Cannot adopt WebKit protocols

#import <WebKit/WebKit.h>
#interface MyClass : NSObject <WebFrameLoadDelegate> {
WebView *webView;
}
cannot find protocol declaration for 'WebFrameLoadDelegate'
WebFrameLoadDelegate is a informal protocol - it is declared as a category of NSObject. To use it you need to declare required methods in class interface and implement them.
When used to declare a protocol, a
category interface doesn’t have a
corresponding implementation. Instead,
classes that implement the protocol
declare the methods again in their own
interface files and define them along
with other methods in their
implementation files.
Directly from Apple Developer Reference:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Protocols/WebFrameLoadDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40003828
...However, depending on the content being loaded, some of the other methods defined in this protocol may be invoked multiple times. All the methods in this protocol are optional.
So the before answer is not correct in the sense of that it is not necessary to implement all the methods.