First, let's explain what a property is: it's basically a set of methods, usually for accessing an instance variable. That's an overly simple (and slightly incorrect) explanation but it will suffice most of the time.
You can define the name of the instance variable with the @synthesize
keyword, as in:
@property Type foo;
...
@synthesize foo = somethingElse;
In this case, the compiler will generate:
- An instance variable called
somethingElse
of type Type
.
- A method to read the variable called
foo
: it just returns the content of the variable somethingElse
.
- A method to write the variable called
setFoo:
: it sets the content of the variable somethingElse
and takes care of notifying key-value observers.
If you don't specify a @synthesize
statement, the compiler will automatically generate an instance variable with the property name prefixed by an underscore. So if your property is named foo
, the automatically created instance variable is called _foo
.
When you do:
@property Student *stObj;
(without a @synthesize
) the compiler generates:
- An instance variable
_stObj
of type Student *
.
- A method called
stObj
that reads the content of the variable _stObj
.
- A method called
setStObj:
that writes the content of the variable _stObj
.
Next, access to instance variables: you can either access them by their name directly, if the scope allows it (like _foo
), or you can access them via the ->
dereference operator as in self->_foo
. The later also allows you to access public instance variables of other objects, as in otherObject->_foo
. Don't do this unless you really know what you're doing, though.
Last but not least, the dot-notation. Writing obj.method
is the same as writing [obj method]
and writing obj.method = value
is the same as writing [obj setMethod:value]
. That is, the dot-notation is a shorter syntax for a method call. (I tend to avoid it since it's also the notation for access struct members, but that's just me.)
With that knowledge, your examples are easy to explain:
In your first example:
stObj = [[Student alloc] init]; // Access to instance variable. OK.
self->stObj = [[Student alloc]init]; // Access to instance variable. OK
// The next statement is the same as [self setStObj:[[Student alloc]init];
// But there is no method named setStObj: defined.
self.stObj = [[Student alloc]init];
// Trying to access a variable that doesn't exist. It's called stObj instead.
_stObj = [[Student alloc]init];
In your second example:
// There is no variable stObj, it's called _stObj in this case.
stObj = [[Student alloc]init ]; // That's why this fails.
self->stObj = [[Student alloc]init]; // And this as well.
// The property has created the `setStObj:` method, so the next
// line succeeds.
self.stObj = [[Student alloc]init];
// The property has created the _stObj instance variable, so the
// next line succeeds.
_stObj = [[Student alloc]init];