Why does a subclass #property with no corresponding ivar hide superclass ivars? - objective-c

The following seems simple enough. There's a superclass with an ivar, and a subclass which accesses the (#protected) superclasses ivar:
// Testclass.h
#interface TestClass : NSObject {
NSString *testIvar;
}
#end
//TestClass.m
#implementation TestClass
#end
//TestSubclass.h
#interface TestSubClass : TestClass {
}
#property (nonatomic, retain) NSString *testProperty;
- (void) testMethod;
#end
//TestSubclass.m
#import "TestSubClass.h"
#implementation TestSubClass
#synthesize testProperty;
- (void) testMethod{
NSLog(#"The value was: %#", testIvar);
}
#end
Simple and correct-seeming enough. However, attempting to compile (for iOS 4.2 SDK, with GCC 4.2) produces this error pointing to the NSLog line: 'testIvar undeclared'.
I'm new to Objective-C, but can't for the life of me see why this should be an error. Comment out the testProperty stuff, and it compiles OK. It seems like adding a synthesized property in a subclass, without a corresponding ivar, is actually hiding an unrelated superclass ivar.
Can anyone enlighten me as to what's happening here? Relatedly, was the compilation error foreseeable? (Foreseeing it would have saved me some time and frustration).

LLVM compiles the source without complaints, switch to LLVM: Select target → Get Info → Build → C/C++ Compiler Version → LLVM 1.5. From my limited experience it’s a better compiler anyway. No idea why GCC behaves the way it does – interesting catch.

The testIvar undeclared error is actually red herring in this case. This message seems to be caused by testProperty not having a corresponding ivar. To resolve the issue either declare a testProperty ivar in TestSubClass.h or make testProperty #dynamic in TestSubClass.m.

Related

How to find unused ivars in Xcode

Sometimes I declare an ivar but after a while I am no longer using it. I would like to remove this sort of cruft from my code, but I cannot find a warning that will show me my unused ivars.
Is there a tool or built in feature of Xcode that will allow me to find all of my unused ivars?
I see that the static analyzer has CLANG_ANALYZER_OBJC_UNUSED_IVARS, but it does not seem to do anything.
#implementation AppDelegate
{
#private
BOOL _foo; // Never read or written to
}
Runing the analyzer in Xcode 5 with CLANG_ANALYZER_OBJC_UNUSED_IVARS (unused ivars) set to YES never produces a warning.
Based on the relevant Clang source code and a couple of quick tests, it seems that the analyzer does not look at ivars that are not both declared in the #interface and marked #private.
#interface Igloo : NSObject
{
NSString * address; // No warning
#private
NSInteger radius; // Warning
}
#end
#implementation Igloo
{
NSInteger numWindows; // No warning
#private // Has no real effect, of course; just testing
NSString * doormatText; // No warning
}
#end
I suggest filing a bug/submitting a patch.
It appears that the static analyzer option only works if you declare the ivar in the header file.
This generates the analyzer warning correctly:
// AppDelegate.h
#interface AppDelegate : NSObject <UIApplicationDelegate>
{
BOOL _foo; // never read or written
}
#end
Neither of these generates any sort of analyzer warning:
// AppDelegate.m
#interface AppDelegate ()
{
#private
BOOL _goo; // never read or written
}
#end
#implementation AppDelegate
{
#private
BOOL _hoo; // never read or written
}
#end
So it looks like you cannot use the modern syntax to keep ivars in the .m file if you want to check for unused ivars.
In Xcode from product menu click on analyze... It will show you unused variables. This will also tell you about dead code.

Use Objective-C without NSObject?

I am testing some simple Objective-C code on Windows (cygwin, gcc). This code already works in Xcode on Mac. I would like to convert my objects to not subclass NSObject (or anything else, lol). Is this possible, and how?
What I have so far:
// MyObject.h
#interface MyObject
- (void)myMethod:(int) param;
#end
and
// MyObject.m
#include "MyObject.h"
#interface MyObject()
{ // this line is a syntax error, why?
int _field;
}
#end
#implementation MyObject
- (id)init {
// what goes in here?
return self;
}
- (void)myMethod:(int) param {
_field = param;
}
#end
What happens when I try compiling it:
gcc -o test MyObject.m -lobjc
MyObject.m:4:1: error: expected identifier or ‘(’ before ‘{’ token
MyObject.m: In function ‘-[MyObject myMethod:]’:
MyObject.m:17:3: error: ‘_field’ undeclared (first use in this function)
EDIT My compiler is cygwin's gcc, also has cygwin gcc-objc package:
gcc --version
gcc (GCC) 4.7.3
I have tried looking for this online and in a couple of Objective-C tutorials, but every example of a class I have found inherits from NSObject. Is it really impossible to write Objective-C without Cocoa or some kind of Cocoa replacement that provides NSObject?
(Yes, I know about GNUstep. I would really rather avoid that if possible...)
EDIT This works:
// MyObject.h
#interface MyObject
#end
// MyObject.m
#include "MyObject.h"
#implementation MyObject
#end
Not very useful though...
It's possible to make classes without a base class. There are a couple of things going on. First, your compiler doesn't seem to like the "()" class extension syntax. Other compilers would be OK with it. If you remove those "()" on line four of MyObject.m then your compiler will complain that you've got two duplicate interfaces for the MyObject class. For the purpose of your test you should move that _field variable into the declaration of MyObject in the header file, like:
#interface MyObject {
int _field;
}
-(void)myMethod:(int)param;
#end
Then you can completely remove that extra #interface in the .m file. That should get you started at least.
It's possible, but note that NSObject implements the memory allocation API in objective-c, and if you don't implement NSObject's +alloc and -dealloc or equivalent on a root class, you'll still need to implement the same functionality for every class.

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

Objective-C: Compiler error when overriding a superclass getter and trying to access ivar

I'm working on building an iOS 6 app.
I have a class TDBeam which inherits from superclass TDWeapon.
The superclass TDWeapon declares a #property in the TDWeapon.h file:
#interface TDWeapon : UIView
#property (nonatomic) int damage;
#end
I do not explicitly #synthesize the property, as I'm letting Xcode automatically do so.
In the subclass TDBeam I override the getter in the TDBeam.m file:
#import "TDBeam.h"
#implementation TDBeam
- (int)damage {
return _damage;
}
#end
Xcode auto-completes the getter method name, as expected. But when I attempt to reference the _damage instance variable (inherited from the superclass), I get a compiler error:
Use of undeclared identifier '_damage'
What am I doing wrong here? I've tried explicitly adding #synthesize, and changing the name of the _damage ivar, but the compiler doesn't "see" it or any other ivars from the superclass. I thought ivars were visible and accessible from subclasses?
Synthesized ivars are not visible to subclasses, whether they are explicitly or automatically created: What is the visibility of #synthesized instance variables? Since they are effectively declared in the implementation file, their declaration isn't included in the "translation unit" that includes the subclass.
If you really want to access that ivar directly, you'll have to explicitly declare it (in its default "protected" form) somewhere that the subclass can see it, such as a class extension of the superclass in a private header.
There are a lot of posts on this topic on Stack Overflow, none of which offer simple concrete advice, but this topic sums it up most succinctly, and Josh's answer is the best in any.
What he kinda stops short of saying outright, is, if this is the kind of thing you want to do, don't use #property at all. Declare your regular protected variable in your base class as he says, and write you're own setters and getters if you need them. The ivar will be visible to any subclasses who can then write their own setters/getters.
At least that's where i've landed on the issue, although I'd a total newb to subclassing.
The idea of creating private headers to host your anonymous category and re-#sythesizing your ivars in your subclass just seems wrong on so many levels. I'm also sure I've probably missed some fundamental point somewhere.
Edit
Okay after some lost sleep, and inspired by Stanford's 2013 iTunes U course, here I believe is an example solution to this problem.
MYFoo.h
#import <Foundation/Foundation.h>
#interface MYFoo : NSObject
// Optional, depending on your class
#property (strong, nonatomic, readonly) NSString * myProperty;
- (NSString *)makeValueForNewMyProperty; //override this in your subclass
#end
MYFoo.m
#import "MYFoo.h"
#interface MYFoo ()
#property (strong, nonatomic, readwrite) NSString * myProperty;
#end
#implementation MYFoo
// Base class getter, generic
- (NSDateComponents *)myProperty {
if (!_myProperty) {
_myProperty = [self makeValueForNewMyProperty];
}
return _myProperty;
}
// Replace this method in your subclass with your logic on how to create a new myProperty
- (NSString *)makeValueForNewMyProperty {
// If this is an abstract base class, we'd return nil and/or throw an exception
NSString * newMyProperty = [[NSString alloc]init];
// Do stuff to make the property the way you need it...
return newMyProperty;
}
#end
Then you just replace makeValueForNewMyProperty in your subclass with whatever custom logic you need. Your property is 'protected' in the base class but you have control over how it is created, which is basically what you are trying to achieve in most cases.
If your makeValueForNewMyProperty method requires access to other ivars of the base class, they will, at the very least, have to be be public readonly properties (or just naked ivars).
Not exactly 'over-ridding a getter' but it achieves the same sort of thing, with a little thought. My apologies if, in trying to make the example generic, some elegance and clarity has been lost.

If a subclass refers to a superclass ivar, synthesizing an unrelated property fails

Edit: I just noticed this other Stack Overflow question asking much the same thing: Why does a subclass #property with no corresponding ivar hide superclass ivars?
This is some interesting behavior that I cannot find documented in anything official or unofficial (blog, tweet, SO question, etc). I have boiled it down to its essence and tested this in a fresh Xcode project, but I can't explain it.
MyBaseClass has an instance variable:
#interface MyBaseClass : NSObject {
NSObject *fooInstanceVar;
}
#end
MySubclass extends MyBaseClass, and declares a totally unrelated property (that is, the property is not intended to be backed by the instance variable):
#import "MyBaseClass.h"
#interface MySubclass : MyBaseClass { }
#property (nonatomic, retain) NSObject *barProperty;
#end
If the implementation of MySubclass does not synthesize the property but implements the accessor methods, everything is fine (no compiler error):
#import "MySubclass.h"
#implementation MySubclass
- (NSObject*)barProperty {
return [[NSObject alloc] init]; // pls ignore flagrant violation of memory rules.
}
- (void)setBarProperty:(NSObject *)obj { /* no-op */ }
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
But if I remove the property accessor methods and replace them with a synthesize declaration for the property, I get a compiler error: 'fooInstanceVar' undeclared (first use in this function).
#import "MySubclass.h"
#implementation MySubclass
#synthesize barProperty;
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
This error goes away if I remove either the synthesize declaration, or if I do not refer to the fooInstanceVar instance variable from within MySubclass.m, or if I put all interface and implementation definitions in a single file. This error also seems to happen in both GCC 4.2 and GCC/LLVM build settings.
Can anyone explain what's happening here?
As replied in this question : objective c xcode 4.0.2: subclass can't access superclass variables "was not declared in this scope"
From the doc : Apple Objective-C Programming Langage :
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF125
The instance variable is accessible within the class that declares it and within classes that inherit it. All instance variables without an explicit scope directive have #protected scope.
However, a public instance variable can be accessed anywhere as if it were a field in a C structure. For example:
Worker *ceo = [[Worker alloc] init];
ceo->boss = nil;
I have the compilation error using LLVM GCC 4.2 (for an iOS project, on device) :
error: 'fooInstanceVar' undeclared (first use in this function)
and the same one using GCC 4.2 :
error: 'fooInstanceVar' undeclared (first use in this function)
I can compile using LLVM Compiler 2.0 whithout error.
For compiling with LLVM GCC 4.2 and GCC 4.2 with the use of self-> :
[NSArray arrayWithObjects:self.barProperty, self->fooInstanceVar, nil];
in the doSomethingWithProperty method.
The compiler is behaving correctly; synthesis in a subclasss using storage in a superclass is verboten.
There was a bug about this filed against llvm at some point. It may be in the publicly accessible bug database.
In any case, please file a bug asking for clarification of this particular rule.
I just tried this and it compiles without warning. What am I not doing?
#interface MyBaseClass : NSObject {
NSObject *fooInstanceVar;
}
#end
#interface MySubclass : MyBaseClass { }
#property (nonatomic, retain) NSObject *barProperty;
#end
#implementation MyBaseClass
#end
#implementation MySubclass
#synthesize barProperty;
- (void)doSomethingWithProperty {
NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil];
NSLog(#"%#", array);
}
#end
It isn't clear what problem you are trying to solve. All instance variables are non-fragile everywhere but 32 bit Mac OS X.
I can't reproduce your error either. Do you have a non-default compiler flag set? Could you provide a copy of your project? It definitely appears to be a bug in the compiler.
Check out this article here for the best use of #property/#synthesize. A quick summary is to remove all of your ivars from your objects (unless you need to use the 32-bit runtime for some reason). Then only use your getters and setters, rather than accessing the synthesized ivars directly. Following this will avoid any future problems with this bug.