is the solution as simple as: only with a property can an ivar be set from 'outside'?
Essentially, yes. Ivars in Obj-C are (by default) "protected", meaning that the compiler won't allow you to access them externally to the object's own code. For example, given the following class declaration:
@interface Dunstable : NSObject
{
NSString * crunk;
}
@end
You might think you'd be able to access the ivar after creating the object, but trying results in an error:
Dunstable * d = [[Dunstable alloc] init];
d->crunk = @"Forsooth"; // Error: "Instance variable 'crunk' is protected
That's why ObjC uses accessor methods. Defining them manually was mandatory before the advent of declared properties:
@implementation Dunstable
- (NSString *)crunk {
return crunk; // implicit ivar access, i.e. self->crunk
}
- (void)setCrunk: (NSString *)newCrunk {
[newCrunk retain];
[crunk release];
crunk = newCrunk;
}
@end
Now, using the @property
and @synthesize
directives creates those accessor methods for you (as well as the variable itself). (The manual memory management in the setter is of course also obsolete under ARC.)
It is possible to make an ivar that's accessible from outside the object:
@interface Dunstable : NSObject
{
@public
NSNumber * nonce;
}
@end
Dunstable * d = [[Dunstable alloc] init];
d->nonce = [NSNumber numberWithInt:2]; // Works fine
but this isn't considered good Objective-C style.
The Objective-C Programming Language doc contains a "Historical Note" about this:
Note: Historically, the interface required declarations of a class’s instance variables, the data structures that are part of each instance of the class. These were declared in braces after the @interface
declaration and before method declarations:
[...]
Instance variables represent an implementation detail, and should typically not be accessed outside of the class itself. Moreover, you can declare them in the implementation block or synthesize them using declared properties. Typically you should not, therefore, declare instance variables in the public interface and so you should omit the braces.
This is a pretty big change (I was actually surprised that there's no syntax given for ivars declared in @interface
anymore in that doc), but it's definitely for the better. You should use declared properties; they do the right thing and make your code cleaner and safer.