Objective-C: Compiler warning for accidental method override - objective-c

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 {}

Related

Method parameters, protocol methods, delegates and respondsToSelector calls

I have this kind of code :
MyClass.h
#import "MyOtherClass.h"
#interface MyClass : NSObject<SomeProtocol, MyOtherClassDelegate> {
...
}
MyClass.m
+ (void) doThis {
[someObject doThisWithDelegate:self]; // someObject is MyOtherClass type
}
MyOtherClass.h :
#protocol MyOtherClassDelegate <NSObject>
#required
// Some methods
#end
- (void) doThisWithDelegate:(id<SomeOtherProtocol>)delegate;
MyOtherClass.m :
- (void) doThisWithDelegate:(id<SomeOtherProtocol>)delegate {
if ([delegate respondsToSelector:#selector(myProtocolMethod:error:)]) do things;
}
Doing like this, I have the following warnings at compile time :
on the line [someObject doThisWithDelegate:self];
"Incompatible pointer types sending Class to parameter of type id<SomeOtherProtocol>"
on the method declaratéin in MyOtherClass.h :
"Passing argument to parameter 'delegate' here"
Before, I hadn't typed the id param (with id<SomeOtherProtocol>), it was just "alone" (id). I noticed that the test :
if ([delegate respondsToSelector:#selector(myProtocolMethod:error:)])
returned FALSE (but of course methods are implemented and declared in the delegate).
So I decided to try to force the id type to conform protocol that causes me that warning at compile time.
What is happening here ?
Why do I have this error, and why do the respondsToSelector do not return TRUE ?
If you want to call respondsToSelector: on a property with a type of id<SomeProtocol> then make sure your protocol conforms to the NSObject protocol.
Example:
#protocol SomeOtherProtocol <NSObject>
// methods
#end
This will then allow you to do:
if ([self.delegate respondsToSelector:#selector(myProtocolMethod:error:)]) {
}
This assumes delegate is defined as:
#property (nonatomic, weak) id<SomeOtherProtocol> delegate;
First make sure you conform to , respondsToSelector is part of NSObject.
Second I would check that i had effectively set the delegate property.
This line :
if ([self.delegate respondsToSelector:#selector(myProtocolMethod:error:)])
should work, since the property delegate is pointing to an object that should have myProtocolMethod:error: ....
I would probably debug with a breakpoint or a test on a setter for the delegate :
-(void)setDelegate:(id)delegate
{
NSLog("We are setting a delegate!: %#", [delegate description]);
_delegate = delegate;
}
If when you run your app, you see the "We setting...." after you do your if line or if you don't see it at all, then you know where your issue is.
Problem was that I've declare the doThis method as a class method and not instance method. So self is not valid when passed as a parameter.

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

Debug / Release build differences for superclass / subclass

I have an iOS project which builds and executes as expected under debug yet throws a compilation error when being built for release. The error is to do with an iVar which is declared in a superclass and it is specifically
'fetchedResultsController_' undeclared (First use in this function).
Here is the superclass .h.
#interface Super : UIViewController <NSFetchedResultsControllerDelegate> {
NSFetchedResultsController* fetchedResultsController_;
NSManagedObjectContext* managedObjectContext_;
}
#property (nonatomic, retain) NSFetchedResultsController* fetchedResultsController;
#property (nonatomic, retain) NSManagedObjectContext* managedObjectContext;
#end
and the superclass .m
#implementation Super
#synthesize fetchedResultsController = fetchedResultsController_;
#synthesize managedObjectContext = managedObjectContext_;
#pragma mark -
#pragma mark Properties
-(NSFetchedResultsController*)fetchedResultsController {
return nil;
}
The Subclass interface is defined thus:-
#interface Sub : Super <UIActionSheetDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
// extra stuff
}
In the subclass .m I implement lazy loading for fetchedResultsController
-(NSFetchedResultsController*)fetchedResultsController {
if (fetchedResultsController_ == nil) { // undeclared error here....
//stuff
}
return fetchedResultsController_;
I'm confused mainly because I don't understand why this would comile in Debug but not in Release!
If someone could identify what the issue is I'd appreciate it greatly
This isn't the answer to your question, but it will make the problem go away.
As things stand, in your Super class, having the instance variable at all is pointless. And you should probably set the property readOnly so people using it know that setting the fetchedResultController property is not allowed. As things stand, people have a reasonable expectation that if they set the property, they'll get more something back when they read it.
So, move the instance variable into the subclass. Declare the property readOnly in the superclass and redeclare it readWrite in the subclass.

Protected methods in Objective-C

What is the equivalent to protected methods in Objective-C?
I want to define methods which only the derived classes may call/implement.
You can simulate protected and private access to methods by doing the following:
Declare your private methods in a class extension (i.e. a unnamed category declared near the top of the class' .m file)
Declare your protected methods in a Subclass header – Apple uses this pattern with respect to UIGestureRecognizer (see documentation and reference to UIGestureRecognizerSubclass.h)
These protections are not, as Sachin noted, enforced at runtime (as they are in Java, for example).
You can neither declare a method protected or private. Objective-C's dynamic nature makes it impossible to implement access controls for methods. (You could do it by heavily
modifying the compiler or runtime, at a severe speed penalty, but for obvious reasons this is not done.)
Taken from Source.
Here is what I did to get protected methods visible to my subclasses, without requiring them to implement the methods themselves. This meant I didn't get compiler warnings in my subclass about having an incomplete implementation.
SuperClassProtectedMethods.h (protocol file):
#protocol SuperClassProtectedMethods <NSObject>
- (void) protectMethod:(NSObject *)foo;
#end
#interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
#end
SuperClass.m: (compiler will now force you to add protected methods)
#import "SuperClassProtectedMethods.h"
#implementation SuperClass
- (void) protectedMethod:(NSObject *)foo {}
#end
SubClass.m:
#import "SuperClassProtectedMethods.h"
// Subclass can now call the protected methods, but no external classes importing .h files will be able to see the protected methods.
I just discovered this and it works for me.To improve upon Adam's answer, in your superclass make an implementation of the protected method in .m file but don't declare it in .h file. In your subclass make a new category in your .m file with the declaration of the protected method of the superclass and you can use the protected method of the superclass in your subclass. This will not ultimately prevent the caller of the supposedly protected method if forced at runtime.
/////// SuperClass.h
#interface SuperClass
#end
/////// SuperClass.m
#implementation SuperClass
- (void) protectedMethod
{}
#end
/////// SubClass.h
#interface SubClass : SuperClass
#end
/////// SubClass.m
#interface SubClass (Protected)
- (void) protectedMethod ;
#end
#implementation SubClass
- (void) callerOfProtectedMethod
{
[self protectedMethod] ; // this will not generate warning
}
#end
Another way using #protected variables.
#interface SuperClass:NSObject{
#protected
SEL protectedMehodSelector;
}
- (void) hackIt;
#end
#implementation SuperClass
-(id)init{
self = [super init];
if(self) {
protectedMethodSelector = #selector(baseHandling);
}
return self;
}
- (void) baseHandling {
// execute your code here
}
-(void) hackIt {
[self performSelector: protectedMethodSelector];
}
#end
#interface SubClass:SuperClass
#end
#implementation SubClass
-(id)init{
self = [super init];
if(self) {
protectedMethodSelector = #selector(customHandling);
}
return self;
}
- (void) customHandling {
// execute your custom code here
}
#end
You can define the method as a private method of the parent class and can use [super performSelector:#selector(privateMethod)]; in the child class.
You can sort of do this with a category.
#interface SomeClass (Protected)
-(void)doMadProtectedThings;
#end
#implementation SomeClass (Protected)
- (void)doMadProtectedThings{
NSLog(#"As long as the .h isn't imported into a class of completely different family, these methods will never be seen. You have to import this header into the subclasses of the super instance though.");
}
#end
The methods aren't hidden if you import the category in another class, but you just don't. Due to the dynamic nature of Objective-C it's actually impossible to completely hide a method regardless of a calling instance type.
The best way to go is probably the class continuation category as answered by #Brian Westphal but you'll have to redefine the method in this category for each subclassed instance.
One option is to use class extension to hide methods.
In .h:
#interface SomeAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
In .m:
#interface SomeAppDelegate()
- (void)localMethod;
#end
#implementation SomeAppDelegate
- (void)localMethod
{
}
#end
I usually name protected method with internal prefix:
-(void) internalMethod;

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

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