Objective-C Not Creating Synthesized Variables - objective-c

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.

Related

Objective-C: Overriding Getter & Setter with Instance Variable (using _) [duplicate]

This question already has answers here:
Error accessing generated ivars when I override setters and getters in Modern Objective-C
(3 answers)
Closed 5 years ago.
I'm learning the Swift programing language and during this I sometimes get in touch with the old Objective-C programming language and its code.
I'm an absolutely beginner and therefore I have some question for better understanding the Setter and Getter.
So, I know that I can create an instance variable through curly braces in the .h file but normally I use properties. These properties are backed by an instance variable and offer automatically a Getter and Setter Method.
Example:
Vehicle .h file:
#interface Vehicle : NSObject
#property int myProperty;
#end
Because I created this property I don't have to declare a Getter and Setter method in the vehicle.m file because they are automatically created by the compiler. So I can create a vehicle-object, set and get the value.
Example
main.m
Vehicle *myvehicle = [[vehicle alloc] init];
[myvehicle myProperty] // myvehicle.myProperty
[myvehicle setMyProperty : 10] // myvehicle.myProperty = 10;
Now I read that it is possible to override the automatically created Getter and Setter method of my created property "myProperty". When declaring my own version of the Getter and Setter I have to declare two methods in the vehicle.h and vehicle.m file. In the vehicle.m file I don't call the object by using the self keyword but by using it's automatically created instance variable (_myProperty). Is it right?
I tried it but alway get an error and I don't know why and what is the point.
Example
Vehicle .h file:
#interface Vehicle : NSObject
#property int myProperty;
-(int) myProperty; //my new Getter method
-(void) setMyProperty: (int)updatedMyProperty; //My new Setter method
#end
vehicle .m file:
#implementation Vehicle
-(int) myProperty {
if (! _myProperty) {
_myProperty = NO;
}
return _myProperty;
}
-(void) setMyProperty: (int)updatedMyProperty {
if (_myProperty == updatedMyProperty) return;
_myProperty = updatedMyProperty;
}
#end
I always get the error "Use of undeclared identifier" and I don't know why. If I understand right I don't have to declare the ivar or its name using #synthesize because the compiler automatically creates the ivar called _myProperty for me. I just have to use #synthesize when I want to change the ivar's name.
I'm not sure why I get stuck and what the point is. Could you explain it?
Thanks in advance!
If you implement all of the accessor methods, the compiler will no longer automatically synthesize the ivar for you. In this case, you have to explicitly do so yourself. E.g.
#synthesize myProperty = _myProperty;
This is only necessary when you have manually implemented all of the accessor methods. The reason is that the compiler is smart enough to know that if you're taking over the accessor methods, you may well not need the ivar, namely that you might be doing something radically different, e.g. computing values from some other property, setting/getting values from some different store, etc. You may want the compiler to synthesize the ivar (in which case you add the above #synthesize statement), but it's equally likely that you've implemented the accessor methods because no backing ivar is needed (in which case you'd omit the above #synthesize statement).
Anyway, staying with your simple example, you get something like:
#interface Vehicle : NSObject
#property (nonatomic) int myProperty; // if you don't write atomic accessor methods, you really should be explicit that this is nonatomic
// as an aside, even if you implement accessor methods, you don't have to declare them here
//
// -(int) myProperty; //my new Getter method
// -(void) setMyProperty: (int)updatedMyProperty; //My new Setter method
#end
And
#implementation Vehicle
// since you implemented all of the accessor properties, you have to manually synthesize the ivar
#synthesize myProperty = _myProperty;
- (int) myProperty {
// do whatever you want here; note, the following doesn't make sense
//
// if (! _myProperty) {
// _myProperty = NO;
// }
return _myProperty;
}
- (void)setMyProperty:(int)updatedMyProperty {
if (_myProperty == updatedMyProperty) return;
_myProperty = updatedMyProperty;
}
#end
Clearly, there's no point in writing these particular accessor methods in the above example, because you're not offering any new functionality, so you wouldn't. You'd just avail yourself of the auto-synthesized accessor methods.
But in those cases that you really need to write your own accessor methods, then you have to explicitly tell the compiler whether you need it to synthesize the ivar for you, too, or not.

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

Is it possible to override property's method?

I need to add some codes when MyClass's string value is changed.
So I'm trying to override a MyClass's string property method.
But I have no idea.. so I do like following.
Is it correct?
How to override property's method?
#interface MyClass{
NSString *string;
}
#property(retain, nonatomic) NSString *string;
#end
#implementation MyClass
- (void)setString:(NSString*)newString{
// want to add some codes.
[self setString:newString]; <= Is this correct?
}
#end
- (void)setString:(NSString*)newString{
// want to add some codes.
string = newString; //This is the setter method!
// You will need to consider memory management etc.
}
What you are probably more up to is this:
- (void)setString:(NSString*)newString{
// want to add some codes.
[super setString:newString];
}
[self setString:newString] at this place would be a an endless recursive loop. [super setString:newString] should call the superlass' setter. However, I never did that myself.
What you are trying to do is not to override (no subclass are involve in your code snippet).
What is in your code is just the declaration #interface and the definition #implementation. It have the same meaning and utility to the declaration/definition in C where you put declaration in a .h file and definition in a .c file.
Assuming that your are speaking of how to synthesize, your code is almost correct. You are just missing the getter - (NSString *) string; or you what it readonly.
As for how to do correctly the assignment you should use the =
- (void)setString:(NSString*)newString{
// want to add some codes.
string = newString; // Correct way of doing it
}
There is 2 way to synthesize a propertie, either by defining the setter and getter by hand or either by using the syntactic sugar #synthesize.
For a better understanding consider that #property and #synthesize are both syntactic sugar. (Only that #property is adding some semantic with the retain, strong, etc.. keyword).
Better is to read the documentation that you can found. There is many easy to found web page where you could have more details.

Why can't I set this to a string?

I've just starting out with obj-c and I created 2 files, a .h and a .m file. The .h file is..
#import <Foundation/Foundation.h>
#interface CardUnit : NSObject
{
#property (assign) NSString *name;
#property (assign) NSString *gold;
#end
and the .m file is
#import "CardUnit.h"
#implementation CardUnit
#synthesize gold = #"gold";
#synthesize name = _name;
#end
But it's giving me an error on
#synthesize gold = #"gold";
Saying "expected ; after synthesize"
Why can't I set that to a string?
#synthesize is not used for giving variables a value, but is rather a shorthand for defining basic getters and setters for the variable. The
#synthesize var = _var
syntax is used to say, "I want you to use the instance variable _var as the internal variable for the property var".
If you want to assign a default string to a property, put it in your init method:
-(id)initWithName:(NSString*)name
{
self = [super init];
if(self)
{
self.gold = #"gold";
self.name = name
}
return self;
}
Or you can set the default value in the getter (per #Mario's comment bellow):
-(NSString*)gold
{
_gold ? return _gold : return #"gold";
}
You got synthesize wrong. It is not for assigning values, it is for generating default setter/getter methods in case you do not provide them. You can use property = ivar to specify which ivar should be used for the property, but a constant value is NOT ivar. So you can't assign string value in this way. Please check The Obj-C Programming Language (Property Implementation Directives) for the details.
You can not assign a value in the #synthesize declaration.
The #synthesize is a declaration that sets getters and setters for variables. and it is not for assigning vales.
You should do later on in a method that will set the value to your "gold" var.
You are not suppose to set value to the variable in synthesize. They are just for synthesize accessor methods.
The #synthesize generates a getter and setter for the property, but the compiler takes care of generating those.
I think it would be a good idea to read a good tutorial on iOS development, maybe start with the Start developing iOS apps today on the Apple Developer connection website.

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.