Syntax for resolving incompatible property type on inherited delegate - objective-c

Some code I inherited has an annoying warning. It declares a protocol and then uses that to specify the delegate
#protocol MyTextFieldDelegate;
#interface MyTextField: UITextField
#property (nonatomic, assign) id<MyTextFieldDelegate> delegate;
#end
#protocol MyTextFieldDelegate <UITextFieldDelegate>
#optional
- (void)myTextFieldSomethingHappened:(MyTextField *)textField;
#end
Classes which use myTextField implement the MyTextFieldDelegate and are called it with this code:
if ([delegate respondsToSelector:#selector(myTextFieldSomethingHappened:)])
{
[delegate myTextFieldSomethingHappened:self];
}
This works, but creates the (legitimate) warning: warning: property type 'id' is incompatible with type 'id' inherited from 'UITextField'
Here are the solutions I've come up with:
Remove the property. This works but I get the warning '-myTextFieldSomethingHappened:' not found in protocol(s)
Drop the protocol entirely. No warnings, but you also lose the semantic warnings if you forget to implement the protocol in the delegate.
Is there a way to define the delegate property such that the compiler is happy?

try:
#property (nonatomic, assign) id<UITextFieldDelegate,MyTextFieldDelegate> delegate;

UITextField has also got property named delegate, but it has another type. Just rename your delegate property to something else.

Found the answer in UITableView.h.
The UIScrollView has property name delegate, and the UITableView has the same name property.
#protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
// Your code
......
#end

The original problem is that there is no information about MyTextFieldDelegate's inheritance during declaration of delegate property. It's caused by forward declaration of protocol (#protocol MyTextFieldDelegate;).
I've faced the same problem but with protocol declaration in the other .h file. In my case solution was just to #import appropriate header.
In your case you just need to swap the order of declaration:
#class MyTextField;
#protocol MyTextFieldDelegate <UITextFieldDelegate>
#optional
- (void)myTextFieldSomethingHappened:(MyTextField *)textField;
#end
#interface MyTextField : UITextField
#property (nonatomic, assign) id <MyTextFieldDelegate> delegate;
#end

Related

Custom delegate for NSWindow

I want to create a custom delegate for NSWindow.
CustomWindow is subclassed to get notified about NSWindowDelegate events.
Now I want to create delegate for this CustomWindow.
I tried following code:
CustomWindow.h
#class CustomWindow;
#protocol CustomWindowDelegate
- (void)method1:(CustomWindow *)sender userInfo:(NSMutableDictionary*) userInfo;
- (void)method2:(CustomWindow *)sender event:(NSEvent *)theEvent;
- (void)method3:(CustomWindow *)sender;
#end
#interface CustomWindow : NSWindow <NSWindowDelegate>
#property (nonatomic) id <CustomWindowDelegate> delegate;
#end
mainDocument.h
#import "CustomWindow.h"
#interface mainDocument : NSDocument
#property (assign) IBOutlet CustomWindow *mainWindow;
#end
mainDocument.m
#import "mainDocument.h"
#implementation mainDocument
- (void)method1:(CustomWindow *)sender userInfo:(NSMutableDictionary*) userInfo
{
...
...
}
- (void)method2:(CustomWindow *)sender event:(NSEvent *)theEvent
{
...
...
}
- (void)method3:(CustomWindow *)sender
{
...
...
}
#end
Its working as per expectations however its giving following warnings:
'retain (or strong)' attribute on property 'delegate' does not match the property inherited from 'NSWindow'
'atomic' attribute on property 'delegate' does not match the property inherited from 'NSWindow'
Property type 'id' is incompatible with type 'id _Nullable' inherited from 'NSWindow'
Auto property synthesis will not synthesize property 'delegate'; it will be implemented by its superclass, use #dynamic to acknowledge intention
How can I get rid of these warnings ?
Any helps are greatly appreciated.
NSWindow already has a delegate property and it uses its delegate for different purposes than you're using yours for. The errors are conflicts between your declaration of your delegate property with the declaration of the inherited property.
The simplest solution is for you to rename your property to customDelegate or something like that. Also, the general convention is for delegate properties to be weak, so you should probably declare yours as weak, too.
In general, one could combine a new delegate protocol with NSWindowDelegate and re-use the existing delegate property. In your case, though, since you've declared CustomWindow to conform to NSWindowDelegate, it seems like you're planning on making the window object its own delegate. So, that would conflict with this approach. But, for completeness, if you were going to do that you'd declare your protocol as an extension of NSWindowDelegate:
#protocol CustomWindowDelegate <NSWindowDelegate>
Your property declaration would have to have the same attributes as NSWindow's declaration of its delegate property. So:
#property (nullable, assign) id<CustomWindowDelegate> delegate;
Finally, since you're relying on NSWindow to actually provide the storage and accessor methods of the property, you'd fix the last warning by putting this in the #implementation of CustomWindow:
#dynamic delegate;

Why do I have to declare a property specified by a protocol in the header, and not the class extension (implementation)

So I have a protocol, which requires a property to be declared:
#protocol MyProtocol <NSObject>
#property MyView *myView;
#end
and an object who conforms to it:
#interface MyViewController : NSViewController <MyProtocol>
#end
However, if I declare the property (specified in the protocol) inside of the implementation file (the class extension):
#interface MyViewController()
#property MyView *myView;
#end
I get this error:
Illegal redeclaration of property in class extension
'MyViewController' (attribute must be 'readwrite', while its primary
must be 'readonly')
There appear to be two main SO threads that address this:
attribute must be readwrite while its primary must be read only
and
Can't declare another window
The first answer doesn't explain anything
The second answer says that you can actually circumvent this error by declaring the property inside of the header; and alas
Header
#interface MyViewController : NSViewController <MyProtocol>
#property MyView *myView;
#end
Implementation
#interface MyViewController()
#end
This builds with no errors.
I also know that when you declare a #property inside of a protocol, it doesn't automatically get synthesized.
So if I wanted to keep the #property declaration inside of the implementation, I would have to #synthesize it. And this also works.
So my question is, why does declaring the #property inside of the header vs the implementation file matter if the #property was initially declared inside of a protocol?
Without the protocol, I thought the only difference was making the #property public or private. But clearly there are other things that happen/don't happen if you declare a #property in the header vs the implementation file
Don't declare there property anywhere in your class. It's already declared in the protocol.
Don't put #property MyView *myView; in either the MyViewController.m or MyViewController.h files.
To fix the warning about "auto property synthesis", you simply add:
#synthesize myView = _myView;
to the MyViewController implementation or add explicit getter and setter methods as needed.

How to properly subclass a delegate property in Objective-C?

In subclassing a class, I want to also subclass a delegate of the parent class given that the subclass now has additional functionality. What's the best way to go about doing this? If I just declare another delegate property in the subclass with the same name I would get a warning "Property type 'id' is incompatible with type 'id' inherited from 'ParentClass'
Given this example that produces the warning:
// Class A
#protocol ClassADelegete;
#interface ClassA : NSObject
#property (nonatomic, weak) id<ClassADelegete> delegate;
#end
#protocol ClassADelegete <NSObject>
- (void)classADidSomethingInteresting:(ClassA *)classA;
#end
// Class B
#protocol ClassBDelegete;
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassBDelegete> delegate; // Warning here
#end
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
Two solutions that remove the warning are.
1) In the subclass, place the protocol definition before the class definition. This is what UITableViewDelegate in UITableView.h does:
// Class B
#class ClassB;
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassBDelegete> delegate;
#end
2) In the subclass, add the original protocol alongside the new one:
// Class B
#protocol ClassBDelegete;
#interface ClassB : ClassA
#property (nonatomic, weak) id<ClassADelegete, ClassBDelegete> delegate;
#end
#protocol ClassBDelegete <ClassADelegete>
- (void)classBDidSomethingElse:(ClassB *)classB;
#end
I assume (1) works as Apple do it this way, Option (2) removes the warning but I haven't compiled and run anything setup this way.
Follow the example of NSTableView and NSOutlineView.
NSOutlineView is a subclass of NSTableView, and defines its own protocol for its dataSource and delegate.
NSTableView declares its delegate this way:
- (void)setDelegate:(id <NSTableViewDelegate>)delegate;
- (id <NSTableViewDelegate>)delegate;
and NSOutlineView:
- (void)setDelegate:(id <NSOutlineViewDelegate>)anObject;
- (id <NSOutlineViewDelegate>)delegate;
Apparently the compiler is more lenient with bare method declarations than it is with property declarations.
Unlike NSTable/OutlineView, you might want to make the subclass's protocol inherit from the base class's protocol, e.g.
#protocol SpecializedProtocol <BaseProtocol>
... it probably depends on the situation.

Does simply conforming to an Objective-C protocol do anything?

CocoaPlant defines a protocol CPCoreDataTraits, analogous to UITexInputTraits like so:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#protocol CPCoreDataTraits <NSFetchedResultsControllerDelegate>
#optional
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#end
If I only want to synthesize the managedObjectContext property for one of my view controllers,
#implementation MyViewController
#synthesize managedObjectContext;
#end
i.e., I don't want to synthesize the fetchedResultsController property or implement any of the NSFetchedResultsControllerDelegate methods, should I still conform to the CPCoreDataTraits protocol, like so?
#interface MyViewController : UIViewController <CPCoreDataTraits>
#end
I.e., as long as I don't synthesize the fetchedResultsController property or implement any of the NSFetechedResultsControllerDelegate methods, then will the end result be exactly as if I had just declared the managedObjectContext property normally, like so?
#interface MyViewController : UIViewController
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#end
As you can see in the protocol declaration, the implementation by your class of the two properties is optional because these two properties have been declared under the #optional statement.
This means that any other class that will use any object conforming to this protocol, must check the effective implementation of an optional method or property before using it.
In the example, any class that wants to access the fetchedResultsController property has to check for the existence of the getter and/or setter methods, e.g. using the:
[myController respondsToSelector:#selector(fetchedResultsController)];
[myController respondsToSelector:#selector(setFetchedResultsController:)];
If the calling method doesn't do this preliminary check and your protocol implementation doesn't support any of these methods (because optional) then the app will raise an exception.
So your approach is correct, the only difference in the two examples is that if you don't use the notation than any call to conformsToProtocol: on your object will return NO.

Overriding properties which conform to protocols

I seem to be getting a new error when using LLVM Compiler 2.0, which I haven't had before.
I have a protocol called DTGridViewDelegate defined as:
#protocol DTGridViewDelegate <UIScrollViewDelegate>
I have a property called delegate on DTGridView (a subclass of UIScrollView, which itself has a delegate property). This is defined as:
#property (nonatomic, assign) IBOutlet id<DTGridViewDelegate> delegate;
Now the message I get is:
DTGridView.h:116:63: error: property type 'id<DTGridViewDelegate>' is incompatible with type 'id<UIScrollViewDelegate>' inherited from 'UIScrollView'
Because I had said that the DTGridViewDelegate conforms to UIScrollViewDelegate, I thought that this would be ok to override this property in this way, and indeed this is the first compiler to suggest there is a problem.
I have fixed the error by declaring the property as such:
#property (nonatomic, assign) IBOutlet id<DTGridViewDelegate, UIScrollViewDelegate> delegate;
I am wondering whether this is a compiler issue?
Your setup looks like the same one used in the case of UITableView inheriting from UIScrollView. The UITableViewDelegate protocol inherits from UIScrollViewDelegate protocol.
I set up the following which compiles fine:
// .h
#protocol ParentClassDelegate
-(NSString *) aDelegateMethod;
#end
#interface ParentClass : NSObject {
id delegate;
}
#property(nonatomic, assign) IBOutlet id <ParentClassDelegate> delegate;
#end
//.m
#implementation ParentClass
#synthesize delegate;
-(id) delegate{
return #"Parent delegate";
}//-------------------------------------(id) delegate------------------------------------
-(void) setDelegate:(id)someObj{
delegate=someObj;
}//-------------------------------------(id) setDelegate------------------------------------
#end
//.h
#protocol ChildClassDelegate <ParentClassDelegate>
-(NSArray *) anotherDelegateMethod;
#end
#interface ChildClass : ParentClass{
}
#property(nonatomic, retain) IBOutlet id <ChildClassDelegate> delegate;
#end
//.m
#implementation ChildClass
//#synthesize delegate;
-(id) delegate{
return #"childDelegate";
}//-------------------------------------(id) delegate------------------------------------
-(void) setDelegate:(id)someObj{
delegate=someObj;
}//-------------------------------------(id) setDelegate------------------------------------
#end
Not sure what is causing your problem. I would note that in the header the UITableViewDelegate protocol looks like:
#protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
... so maybe the compiler likes things more explicit sometimes.
I would suggest a clean and build. That solves a lot of problems.
Since there isn't a formal Objective-C language specification, it's impossible to say whether the compiler is behaving properly. All we can say is that Apple's gcc doesn't seem to have a problem with the above scenario, though it's conceptually unsound as it can break Liskov substitution, since delegate is covariant from UIScrollView to DTGridView (though covariance is just as much a problem). What would happen if you passed a DTGridView to code expecting a UIScrollView, which then proceeded to set delegate to an object that conformed to UIScrollViewDelegate but not DTGridViewDelegate?