Reputation: 61834
I would like to understand it very well, but so far I didn't find complex answer considering all possibilities in one place. I know that in modern objective-c we don't create ivars just after @implementation Album
between { }
.
Just for tests I created Album.h:
@interface Album : NSObject
@property (nonatomic, copy, readonly) NSString *title, *artist;
@property (nonatomic, copy) NSString *title2, *artist2;
- (id)initWithTitle:(NSString*)title;
@end
And Album.m:
@implementation Album
- (id)initWithTitle:(NSString*)title {
self = [super init];
if (self) {
//READ-ONLY
title = title; //1, still nil after compile
_title = title; //2,
self.title = title; //3, "assignment to readonly property"
self._title = title; //4, "property '_title' not found"
artist = @"Shakira"; //5, "use of undeclared identifier 'artist'"
_artist = @"Shakira"; //6
self.artist = @"Shakira"; //7, "assignment to readonly property"
self._artist = @"Shakira"; //8, "property '_artist' not found"
//READ-WRITE
title2 = title; //9, "use of undeclared identifier 'title2'"
_title2 = title; //10
self.title2 = title; //11
self._title2 = title; //12, "property '_title2' not found"
artist2 = @"Shakira"; //13, "use of undeclared identifier 'artist2'"
_artist2 = @"Shakira"; //14
self.artist2 = @"Shakira"; //15
self._artist2 = @"Shakira"; //16, "property '_artist2' not found"
}
return self;
}
@end
Now I use it:
Album *album = [[Album alloc] initWithTitle:@"Live from Paris"];
The questions are:
Upvotes: 3
Views: 442
Reputation: 16660
Some explanations at the beginning
A. @property
declares methods with the name -property
and, if it is not read only, -setProperty:
. Nothing else is done by @property
itself!
B. @property
does not synthesize an ivar. What? Say it again! @property
does not synthesize an ivar. (I can repeat that as many times as you want.)
Ivars are synthesized, when you use an explicit @synthesize
or – and that makes it a little bit difficult to understand – when you have auto synthesize. But there is still an (implicit, unshown) synthesize!
To have an implicit synthesize, the following conditions have to be met:
Since you can see the first condition in source code and cannot see the second condition, it is easy to assume that @property
synthesized the ivar. But it is not true. Let's try it:
@interface Foo : NSObject
@property NSString *foo;
@end
@implementation Foo
// Both accessors are implemented -> no auto synthesize -> no ivar
- (NSString*)foo
{
return _foo; // Error: No ivar _foo
}
- (NSString*)setFoo:(NSString*)foo
{
_foo=foo; // Error: No ivar _foo
}
@end
The reason is: If @property
itself would synthesize the ivar, it would be impossible to have a declared property that is not backed with an ivar.
(Additionally no ivar is synthesized, if you already have one that fits. But we do not need that here.)
C. An synthesized ivar has the identifier _property
, not property
. (In some cases a existing ivar without underscore would be taken as an already existing ivar, but we do need that here, too.)
D. The dot notation does not access an ivar. It sends a message. If the statement is an lvalue, is uses the setter, if it is a rvalue, it uses the getter. You can simply translate that:
self.property = …; // [self setProperty:…];
… = self.property; // … = [self property];
Why 4, 8, 12, 16 properties not found while 2, 6, 10, 14 exists?
At 4, … you use dot notation, send a message with the selector set_Property
. There is no such method, because the synthesized method is -setProperty:
: Error
At 2, … you access the ivars directly. It's identifier is _property
. Everything is fine.
What is the difference between 10 and 11 or 14 and 15?
At 11, 15 you set the value of the property using a setter. At 10, 14 you set the ivar directly. The code of the setter is not executed.
Let's have an example:
@interface Foo : NSObject
@property NSString *foo;
@end
@implementation Foo
// ivar _foo is (auto) synthsized, because there is no getter.
- (void)setFoo:(NSString*)foo
{
NSLog( @"Setter executed");
_foo = foo;
}
@end
In line 10, 14 you will see no log, because the setter is not executed. In line 11, 15 you will see a log.
If you have additional things to do in the setter (or getter) accept of only setting the ivar, the result will differ.
What I actually do in 1?
title
is not an ivar (the identifier of the ivar is _title
), but a parameter var. (Look at the head of the method definition.) You assign the value of the parameter var to itself, what is a kind of meaningless.
You assign nothing to the ivar, because you do not use the ivar.
Where did I declare properties 2, 6, 10, 14?
See above A. and B.
Why I can assign to readonly properties in 2, 6 but not in 3, 7?
The ivar is never read only. The readonly
and readwrite
options are for the method declaration. (See above A. - C.)
In 2, 6 you set the ivar directly. It cannot be read only, so everything is pretty fine.
In 3, 7 you send a message to a method that does not exists, because the property is read only.
Upvotes: 4
Reputation: 4728
when you type
self.title = @"foo";
it is really shorthand for
[self setTitle:@"foo" ];
You don't implement
-(void)setTitle:(NSString *)title
the compiler has synthesised it for you. If you could see what it has put there it might look something like this
-(void)setTitle:(NSString *)title{
_title = [title copy];
}
So you see _title is an instance variable. title is not, it is a method which returns an NSString. It has been synthesised for you, but it looks like this
-(NSString *)title{
return _title;
}
They are fundamentally different things. If title is readonly then the setTitle: method is never synthesised, although the _title iVar is still there. So when you type self.title = @"bar" the compiler looks for -(void)setTitle .. which is not there. -> unhappy compiler
The best practice is to avoid using the _underScore variables (known as backing iVars) directly. Always use self.title = @"some Title" instead. If you ever want to use key value observing, which is one of cocoas best features, you'll be glad you did.
Upvotes: 4