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

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.

Related

Objective-C Not Creating Synthesized Variables

I'm a beginning iOS developer, and still getting accustomed to this concept of synthesized variables and XCode automatically creating variables and setter/getter methods. I did quite a bit of research but was not able to find an answer that addressed what I'm facing.
I created a header class as follows:
#import "Card.h"
#interface PlayingCard : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
#end
And I have the following implementation class:
#import "PlayingCard.h"
#implementation PlayingCard
- (NSString *)contents
{
NSArray *rankStrings = #[#"?",#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"J",#"Q",#"K"];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void)setSuit:(NSString *)suit
{
if([#[#"♥︎",#"♦︎",#"♠︎",#"♣︎"] containsObject:suit]) {
_suit = suit;
}
}
- (NSString *)suit
{
return _suit ? _suit : #"?";
}
#end
My error is, whenever I use the _suit variable I get an error from XCode saying:
Use of undeclared identifier '_suit'; did you mean 'suit'?
It was my understanding that _suit is generated automatically by the compiler and I should be able to access the "suit" property defined in the header file with "_suit". Is it because I'm overriding the compiler's automatically generated setter and getter methods? Changing "_suit" to "self.suit" seems to fix the problem, but I'm confused as to why it seems that my underscore synthesized variable is not being generated. Any insight to this would be greatly appreciated, thanks!
If you manually create both accessors (the setter and the getter) for an #property, the compiler assumes you don't need/want it to synthesize them -- and the corresponding instance variable -- for you. There are two possible solutions. Either declare the instance variable yourself:
#implemntation PlayingCard
{
NSString *_suit;
}
Or, my preferred approach, use an explicit #synthesize statement above your custom accessors to tell the compiler that you do still want to synthesize an instance variable for the property:
#synthesize suit = _suit;
Note that the = _suit is necessary because for legacy reasons, a simple #synthesize suit; will default to naming the ivar suit without the underscore prefix.

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.

Is there any way to add an iVar that's not in the header file (not using LLVM 2.0 or later) in Objective-C?

I recently learned that you can add ivar in a class extension with LLVM2.0. (gcc can't do this)
This is somehow really private iVar because other users don't it's existence since it's not in the header file.
like:
//SomeClass.h
#interface SomeClass : NSObject {
}
#end
//SomeClass.m
#interface SomeClass ()
{
NSString *reallyPrivateString;
}
#end
#implementation SomeClass
#end
But this does rely on the compiler. Is there any other way to declare an ivar that's not in the header file?
The only place to declare instance variables is in the interface or a class extension (which is really an extension of the interface). But you can effectively add instance variables at any time with the modern runtime using the associated object functions.
If you are implementing a library and want to hide your instance variables take a look at what Apple does in the interface for UIWebView. They have an internal webview that does not expose a header file.
#class UIWebViewInternal;
#protocol UIWebViewDelegate;
UIKIT_CLASS_AVAILABLE(2_0) #interface UIWebView : UIView <NSCoding, UIScrollViewDelegate> {
#private
UIWebViewInternal *_internal;
}
If you're just going to be using the ivar internally, and you're using the modern runtime (Snow Leopard 64 bit and iOS 3.0+, I think) then you can just declare properties in a class extension and synthesize them inside the class. No ivars are exposed in your header, no messy id _internal objects, and you get around fragile ivars, too.
// public header
#interface MyClass : NSObject {
// no ivars
}
- (void)someMethod;
#end
// MyClass.m
#interface MyClass ()
#property (nonatomic, retain) NSString *privateString;
#end
#implementation MyClass
#synthesize privateString;
- (void)someMethod {
self.privateString = #"Hello";
NSLog(#"self.privateString = %#", self.privateString);
NSLog(#"privateString (direct variable access) = %#", privateString); // The compiler has synthesized not only the property methods, but also actually created this ivar for you. If you wanted to change the name of the ivar, do #synthesize privateString = m_privateString; or whatever your naming convention is
}
#end
This works with Apple's gcc, in addition to LLVM. (I'm not sure if this works on other platforms, ie not Apple's gcc, but it will certainly work for both iOS and Snow Leopard+).

Objective-C setter/getter naming conventions drive me mad?

I have been trying to understand something for several hours and I would like to get your point of view.
I have setter/getter on one of my class properties (I noticed that I MUST add "set" in front of the setter name else the compiler says that there is no setter):
#property (nonatomic, retain, readwrite, setter=setTopString:, getter=TopString) NSString* m_topString;
When I call the setter like this, the compiler is happy:
[secureKeyboardController setTopString:#"This action requires that your enter your authentication code."];
But when I try to use the "dot" convention, then I am rejected by the compiler:
secureKeyboardController.topString = #"This action requires that your enter your authentication code.";
What is really weird is that the dot naming convention works fine with this property:
#property (nonatomic, readwrite, getter=PINMaxLength, setter=setPINMaxLength:) NSInteger m_PINMaxLength;
In this case i can do:
[secureKeyboardController setPINMaxLength:10];enter code here
or
secureKeyboardController.PINMaxLength = 10;
In both cases, the compiler is happy.
I really would like to fall asleep tonigh less stupid than I currently feel now. Thus any explanation would be greatly appreciated.
Regards,
Apple92
What you're doing is declaring properties as if you were declaring instance variables. You should not be using the names in the getter and setter attributes on the #property declaration with dot syntax; that it happens to be working now is not - so far as I know - by design.
The property should be what you use with dot syntax. For some reason - unfamiliarity with Cocoa coding conventions, I expect - you named your properties m_topString and m_PINMaxLength. That means you should use them as someObject.m_topString and someObject.m_PINMaxLength.
If you want to use those names for the instance variables that you've decided to use for the properties' backing storage, you should declare that in the #synthesize directive instead.
This is how your class should look, to be more in line with regular Cocoa and Objective-C coding conventions:
#interface SomeClass : NSObject {
#private
NSString *m_topString;
}
#property (nonatomic, readwrite, copy) NSString *topString;
- (id)initWithTopString:(NSString *)initialTopString;
#end
#implementation SomeClass
#synthesize topString = m_topString;
// this says to use the instance variable m_topString
// for the property topString's storage
- (id)initWithTopString:(NSString *)initialTopString {
if ((self = [super init])) {
m_topString = [initialTopString copy];
// use the ivar directly in -init, not the property
}
return self;
}
- (void)dealloc {
[m_topString release];
// use the ivar directly in -dealloc, not the property
[super dealloc];
}
- (NSString *)description {
return [NSString stringWithFormat:#"SomeClass (%#)", self.topString];
// elsewhere in your class, use the property
// this will call through its getter and setter methods
}
#end
You are trying to fight the compiler, and the compiler fights back.
You are trying to declare a property named m_topString with setter setTopString and getter TopString, and that is plainly stupid. You are writing Objective-C code, not C++. Your code will be a maintenance nightmare (unless the next maintainer is just sensible and changes your code to Objective-C conventions).
Do yourself a favour, start writing Objective-C code. Just call the property topString, don't pick your own names for the setter and getter, don't pick your own names for the instance variable, and everything works just fine.
Capitalize the T in TopString, i.e. secureKeyboardController.TopString
I'm 90% sure that will fix your problem.

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

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.