Reputation: 4980
I've been writing Objective-C for a few years now, and decided to go back and learn the very basics to help me write even better code. I'm trying to learn all about instance variables, inheritance and class extensions. I've been reading up on all three, but there is one thing that boggles my mind. I have a simple app that contains 2 classes, Person, Male (inherits from Person), and of course Main (which imports the Male class, therefore being able to access the instance variables found in both Person and Male).
The code is simple, and for the sake of space I won't post all of it. Basically Main takes these variables and plays around with them. This is the part that is boggling my mind:
@interface Person : NSObject {
float heightInMeters;
int weightInKilos;
}
@property float heightInMeters;
@property int weightInKilos;
@end
When I delete the brackets and variable declarations, leaving it like this:
@interface Person : NSObject
@property float heightInMeters;
@property int weightInKilos;
@end
The code still inherits and executes just fine.
1. What is the point of even declaring them there in the first place if we can just create two properties?
2. why create two instance variables AND properties to correspond with them?
3. I know that we can declare the variables in the .m instead to keep them private to the class and everything that subclasses it. like this:
@implementation Person {
float heightInMeters;
int weightInKilos;
}
What is the difference here? I feel like I'm missing a lot of basics. Is there a simplistic way of putting this all in perspective?
Upvotes: 0
Views: 185
Reputation: 1217
When you declare a @property
, the compiler will automatically synthesize the variable prefixed with an underscore, a getter method, and a setter method.
@interface MyClass ()
@property(strong, nonatomic) NSString *myString;
@end
In this example the compiler would syhtnesize the variable as _myString
, the getter as
-(NSString *)myString
and the setter as
-(void)setMyString:(NSString *)string
The keywords after "@property" (strong, nonatomic)
define the property's attributes. strong
, the default, implies ownership, meaning that in this case MyClass
instances will essentially be responsible for the retain/release of their respective myString
objects. nonatomic
means the variable is not guaranteed to always be a valid value in a multithreaded environment, for example if the getter is called at the same time as the setter.
Additionally, the compiler will treat dot syntax used to retrieve/set instance variables as calls to the appropriate getter/setter methods. Therefore, given an instance of MyClass
MyClass *exampleClass = [[MyClass alloc] init];
Both of the following are equivalent statements:
NSString *string1 = example.myString; // dot syntax
NSString *string1 = [example myString]; // explicit call to the getter method
For further reading, take a look at Apple's Programming with Objective-C Guide.
As for your specific questions:
It's actually not a good idea to declare variables explicitly as public variables in your MyClass.h
file (or in most other cases). Instead, declaring them as properties automatically creates a private variable (and accessor methods), making adhering to OOP best practices a little easier. So there is no point in declaring
// MyClass.h
@interface MyClass : NSObject {
NSString *myString // public variables not good
}
Also because of what I stated above regarding dot syntax, if you use self.myString
internally in MyClass.m
or instanceOfMyClass.myString
externally, the public variable myString
will never even be touched because the synthesized variable is named _myString
.
See above--you don't need two instance variables, only one.
If you declare your variables privately in the @implementation
part of your .m
file, the compiler won't be able to help you by synthesizing the getters and setters. Even as private methods, getters and setters can help reduce complexity in your code, for example checking for the validity of variable values. (Note: you can override accessor methods.)
// MyClass.m
@interface MyClass () // private interface
@property(nonatomic, strong) NSString *myString;
@end
@implementation MyClass {
// no more need for private variables!
// compiler will synthesize NSString *_myString and accessors
}
-(void)setMyString:(NSString *)string { // overwrite setter
// no empty strings allowed in our object (for the sake of example)
NSAssert([string length] > 0, @"String must not be empty");
// assign private instance variable in setter
_myString = string;
}
@end
This way, even when you subclass MyClass
, the subclass will inherit the getter and setter methods that were synthesized for us by the compiler.
Upvotes: 2