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:
1. What is the point of even declaring them there in the first place if we can just create two properties?
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
.
2. Why create two instance variables AND properties to correspond with them?
See above--you don't need two instance variables, only one.
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. 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?
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.