Can an inherited #property not satisfy a <protocol> #property? - objective-c

I've got a protocol:
#protocol Gadget <NSObject>
#property (readonly) UIView *view;
- (void) attachViewToParent:(UIView *)parentView;
#end
And an "abstract" base class, with an implementation (as a getter, not shown) of -(UIView *)view:
// Base functionality
#interface AbstractGadget : NSObject {
UIView *view;
}
#property (readonly) UIView *view;
#end
But when I implement the Gadget protocol in a subclass of AbstractGadget, like so:
// Concrete
#interface BlueGadget : AbstractGadget <Gadget> {
}
- (void) attachViewToParent:(UIView *)parentView;
#end
#implementation BlueGadget
- (void) attachViewToParent:(UIView *)parentView {
//...
}
#end
I get a compiler error telling me "warning: property 'view' requires method '-view' to be defined." I can make this go away using #dynamic, or adding a stub method:
- (UIView *) view {
return [super view];
}
But I just want to know if I'm doing something that's not supported, something I shouldn't be doing, or if it's just a limitation / bug in the compiler?

By declaring the property as #dynamic you are telling the compiler that the property getter (and setter if required) are implemented elsewhere (potentially at runtime). This sounds like a perfectly reasonable use case to me.
See The Docs for more information.

I also came across this exact issue. This is one of situations that #dynamic is there for.

Here is the rule for variable, property and synthesize in objective-C:
If you have a property, you must have a #synthesize or you declare #dynamic and write the getter and setter method yourself.
So, because you have a property called view, you have to declare #synthesize. That should be it. Nothing to do with #protocol, inheritance

Related

Objective-C: Compiler warning for accidental method override

I just found a bug in our iOS App which was triggered by an accidental method override.
In this case a property in a Subclass override a "private" method in the Superclass.
#interface MyClass : NSObject
- (void)doSomething;
#end
#implementation MyClass
- (void)doSomething {
[self hideView];
}
- (void)hideView {
}
#end
#interface MySubclass : MyClass
#property (NS_NONATOMIC_IOSONLY) IBInspectable BOOL hideView;
#end
#implementation MySubclass
#end
If [self hideView] is called within the doSomething method, the hideView method is not called. Instead just the property is asked for its value. I understand why this is happening but this is a error prone situation since the subclass is not aware of the hideView method.
My question is how to prevent those issues? Is there a compiler warning?
You can not completely prevent those issues, this is the intended behaviour in Objective C. However, if you want to protect your private methods from accidental override, you can prefix their names with an identifier of your library (or any other string that you like), for example:
- (void)__mylib_hideView {}

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;

Auto property synthesis will not synthesize property declared in a protocol even though its implemented

I got that error:
/business/Dropbox/badgers/BadgerNew/BGProfileView.m:56:17: Auto property synthesis will not synthesize property declared in a protocol
I am aware that auto property synthesis will not synthesize property declared in a protocol
So I synthesize that my self.
This is the protocol:
#protocol BGIhaveNavigationController <NSObject>
#property (readonly)UINavigationController * navigationController;//This is the problematic property
#end
#protocol BGCommonProtocol <NSObject>
#end
As for the implementation
#interface BGProfileView : UIViewController
#end
is a UIViewController and as we know UIViewController has a navigationController property. So what's wrong?
Things get problematic when I use this:
#interface BGProfileView () <BGReviewsTableDelegateProtocol>
BGReviewsTableDelegateProtocol protocol inherits BGIhaveNavigationController protocol
I can remove the warning by adding:
-(UINavigationController *) navigationController
{
return self.navigationController;
}
But that's absurd. In a sense
-(UINavigationController *) navigationController
{
return self.navigationController;
}
-(UINavigationController *) navigationController already exist through UINavigationController
Use
#dynamic navigationController;
This tells the compiler that the property implementation is 'somewhere else' and that it should trust that the requirement will be fulfilled at runtime. In practice, that means its in the superclass.
If you try to implement it yourself you'll end up with duplicate storage (so things probably won't work how you expect) or recursion.

How do I know if not declaring #synthesize will result in "use of undeclared identifier"?

In one of the assignments, I had to override the superclass's getter method for the game logic (so the method will get the subclass of the game logic instead of the original one).
CardGameViewController.h:
#import <UIKit/UIKit.h>
#import "Deck.h"
#import "CardGame.h"
#interface CardGameViewController : UIViewController
#property (nonatomic) NSUInteger startingCardCount; // abstract
#property (strong, nonatomic) CardGame *game;
- (Deck *)createDeck; // abstract
- (void)updateCell:(UICollectionViewCell *)cell usingCard:(Card *)Card; // abstract
#end
CardGameViewController.m:
#import "CardGameViewController.h"
...
// no #synthesize here, but works fine.
- (CardGame *)game
{
if (!_game) _game = [[CardGame alloc] initWithCardCount:self.startingCardCount
usingDeck:[self createDeck]];
return _game;
}
...
#end
SetCardGameViewController.m:
...
#interface TSSetCardGameViewController()
#property (strong, nonatomic) CardGame *game;
#end
#implementation TSSetCardGameViewController
#synthesize game = _game; // Compiler *will* complain if this line is commented out.
- (CardGame *)game
{
if (!_game) _game = [[SetCardGame alloc] initWithCardCount:self.startingCardCount
usingDeck:[self createDeck]];
return _game;
}
...
#end
Then I got "Use of undeclared identifier" for "_game". so I declared
#property (strong, nonatomic) CardGame *game;
But I got the same error, so I used "self.game" instead, which caused a bad access exception.
I couldn't find anything on Google, so I tinkered around until I found that this solves the problem:
#synthesize game = _game;
Now, my question is why. My understanding is the new version of Xcode does the synthesizing for me, unless I override both its getter and setter. I did override the getter, but not the setter, so Xcode technically should have included it automatically. The proof is that Xcode did not complain until I subclassed CardGameViewController and specifically overrode the getter method. (FYI neither CardGameViewController nor its subclass had a setter method for *game)
So I'm a little confused. Please help!
The problem here is that you have two versions of _game. Since the introduction of the new ABI (64-bit Mac and all iOS), each subclass can create its own ivars without tromping all over its superclass's ivars (even if they're named the same). And ivars created by #synthesize are private. Now hold that thought and let's see what's happening:
In your superclass, you declare a property that has a getter and setter (though you almost certainly don't mean to have a setter…) You override the getter. The compiler says "but you still want me to create a setter for you, so I'll create an ivar to match it."
In your subclass, you declare no new properties. You may think you do, but it's just the same property that comes from the superclass; it's not a new property. There's already a getter and setter in the superclass, so there's no need for the compiler to create an ivar.
You then reference an ivar that does not exist in your subclass. It only exists as a private ivar in the superclass. The compiler can't see that (and wouldn't let you access it even if it could).
The typical solution to this problem is, rather than overriding -game, just provide a class method called +gameClass and have it return the correct class to instantiate. (See +layerClass in UIView for an example of this pattern.)

Error accessing generated ivars when I override setters and getters in Modern Objective-C

I know now the new Objective-C compiler lets you not need to synthesize your properties anymore. I have one file that has two classes in it. My .h for a simple helper class looks like this:
#interface ViewFrameModel : NSObject
#property (nonatomic, strong) UIView *view;
#property (nonatomic, assign) CGRect frame;
- (id)initWithView:(UIView *)view frame:(CGRect)frame;
#end
In the same .h file, for my other class (class 2), I have:
#property (nonatomic, strong) ViewFrameModel *viewFrameModel;
In class 2.m, I can do this:
- (void)setViewFrameModel:(ViewFrameModel *)viewFrameModel {
_viewFrameModel = viewFrameModel;
[self pushViewFrameModel:viewFrameModel];
}
This works fine with no complaints from the compiler, however, when I add this:
- (ViewFrameModel *)viewFrameModel {
return _viewFrameModel;
}
I get two complaints, one on the first method setViewFrameModel:
"Use of undeclared identifier _viewFrameModel, did you mean viewFrameModel"
And the other on return _viewFrameModel:
"Use of undeclared identifier _viewFrameModel, did you mean viewFrameModel"
"Reference to local variable viewFrameModel' declared in enclosing context"
Why do I get these errors when I add in the
- (ViewFrameModel *)viewFrameModel {
return _viewFrameModel;
}
method? I want to override this method with some custom info, but it's complaining at me :-. Thoughts? TIA.
If you override both the setter and the getter, the compiler will not automatically create the instance variable for you anymore. You can add it to your class implementation like so:
#implementation ClassName {
ViewFrameModel *_viewFrameModel;
}
...
#end
Here is the results of some testing I did last year: iOS automatic #synthesize without creating an ivar.
In short, you need to use #synthesize or declare an iVar explicitly.
To summarize the answers:
If you override both the setter and the getter, the compiler will not create the instance variable for you.
Why? In that case, the compiler assumes that the property is dynamic: that it might be a property that relies on other properties for storage / computation, or that it will be created in other ways, for example, at runtime using Objective-C runtime functions.
To help the compiler understand the situation better there are two potential solutions:
#implementation Class
#synthesize property = _property;
...
#end
or
#implementation Class {
PropertyClass *_property;
}
...
#end