See Also
Variables in Objective-C
Last time we talked about local variables and function parameters. Today we will look at instance variables of Objective-C classes and we'll touch on methods as well.Variables in Objective-C
Objective-C classes are defined by an
@interface
section and an @implementation
section. The @interface
section declares the instance variables and looks like this:@interface Cat : NSObject { NSString *name; int age; } - (id)initWithName:(NSString *)aName andAge:(int)anAge; // ... @endThe instance variables (or ivars) are declared between the curly braces, after the class name and superclass but before method declarations. Instance variables are declared like any local or global variable, but have a different scope. By default, instance variables are visible in all instance methods of a class and its subclasses. (Instance methods are the ones that begin with a minus sign.) The implementation of the
-init
method might look like this:// example of using instance variables @implementation Cat - (id)initWithName:(NSString *)aName andAge:(int)anAge { self = [super init]; if ( ! self) return nil; name = [aName copy]; age = anAge; return self; } // ... @endThis is a pretty standard Objective-C
-init
method. Note that unlike JavaScript, Python, Ruby and many other languages, there's no need to prefix instance variables with this
, self
, @
or something similar. But this leads to a problem: what if a method parameter has the same name as an instance variable? If our -init
method was defined like this:// example of name shadowing - (id)initWithName:(NSString *)name andAge:(int)age { self = [super init]; if ( ! self) return nil; name = [name copy]; // causes a warning age = age; // causes a warning return self; }If you compile this code, you'll see warnings like
local declaration of 'name' hides instance variable
. You should heed these warnings! Code likeage = age;assigns parameter
age
to itself, leaving the instance variable age
unchanged.Unfortunately there's no elegant way to deal with parameters shadowing instance variables. Objective-C does allow you to use the "pointer to member" or "arrow" (
->
) operator on self
to access instance variables, like this:// example of pointer to member - (id)initWithName:(NSString *)name andAge:(int)age { self = [super init]; if ( ! self) return nil; self->name = [name copy]; // still causes a warning self->age = age; // still causes a warning return self; }If you know C++ or Java, you would think this would work, but the Objective-C compiler still produces a warning. This is probably one of the most niggling little differences between Objective-C and other languages that you just need to get over. The only practical solution is make sure that parameter names don't clash with instance variables. For initializers and setters, most Objective-C programmers simply prefix their parameter names with "
a
", "an
" or "the
". It's not very elegant, but it works. If you're tempted to simply prefix your instance variable names with an underscore, I recommend against that. Apple uses the leading underscore convention in framework classes in order to prevent name clashes when application writers have to extend framework classes.The
struct
behind the curtainIf you're familiar with C or C++, seeing an expression like
self->name
should give you a clue to the inner workings of Objective-C. Underneath, Objective-C objects are pretty much just struct
s and functions. The @interface
of our Cat
class:@interface Cat : NSObject { NSString *name; int age; } // ... @endbecomes a
struct
that looks something like:// pseudocode for struct generated for Cat class struct Cat { Class isa; // inherited from NSObject NSString *name; int age; };The instance variables you define are tacked on to those defined by the superclass, and its superclass and so on. So if we defined a subclass of
Cat
:@interface LolCat : Cat { UIImage *picture; NSString *caption; int upVotes; } @endthe Objective-C compiler would generate a structure in memory that looked something like:
// pseudocode for struct generated for LolCat class struct LolCat { Class isa; // inherited from NSObject NSString *name; // inherited from Cat int age; // inherited from Cat UIImage *picture; NSString *caption; int upVotes; };Similarly, Objective-C methods are simply regular C functions underneath with extra parameters automatically added by the compiler. So our
-init
method- (id)initWithName:(NSString *)aName andAge:(int)anAge { // ... }is compiled into something that resembles:
// pseudocode for function generated for -initWithName:andAge: method id initWithName_andAge(id self, SEL _cmd, NSString *aName, int anAge) { // ... }The parameters
self
and _cmd
are added to each instance method by the compiler. Naturally, self
is a pointer to the memory for the instance, organized like a struct
as we've shown. The _cmd
parameter holds the method selector (which is basically the method name) and can be used to do very crazy dynamic stuff we won't dive into today.Instance variable scope
We mentioned earlier that by default, instance variables are visible in all instance methods of a class and its subclasses. This is referred to as protected scope in Objective-C. You can change the scope of instance variables to be private, public or package as well as protected, but in general these other scopes aren't used very frequently in Objective-C. Going back to our
Cat
example, you would use the scope specifiers like this:@interface Cat : NSObject { // protected by default double weight; @private int lives; @protected int age; @public NSString *name; @package UIColor *color; } // ... @endPrivate scope restricts visibility of the instance variable to the class it's defined in; subclasses are not allowed to use it. Protected is the default if you don't specify a scope, and allows all subclasses to read and write to the instance variable. Public scope is rarely seen in Objective-C; the pointer to member or arrow (->) operator is used to access public instance variables:
// accessing public instance variables Cat *cat = [[Cat alloc] init]; NSLog(@"The cat's name is %@", cat->name);Package scope is used by framework creators; it makes the instance variable public within the framework and private outside it.
Properties
Since instance variables aren't visible outside classes by default, most Objective-C programmers create getters and setters when they want to expose instance variables. Before Objective-C 2.0, these were written by hand for each instance variable:
// getter and setter declarations @interface Cat : NSObject { NSString *name; int age; } - (NSString *)name; - (void)setName:(NSString *)aName; - (id)age; - (void)setAge:(int)anAge; // ... @endThe normal Objective-C convention is to give the getter the same name as the instance variable and to prefix the setter with "set". The implementation of simple setters is boilerplate but not trivial due to the need to manage retain counts:
// getter and setter definitions @implementation Cat - (NSString *)name { return name; } - (void)setName:(NSString *)aName { if (name != aName) { [name release]; name = [aName copy]; } } // ... @endWriting setters manually is both laborious and error prone, so Objective-C 2.0 introduced properties, allowing getters and setters to be generated automatically. Rewriting the
Cat
class using properties:// property declaration @interface Cat : NSObject { NSString *name; int age; } @property(copy, nonatomic) NSString *name; @property(assign, nonatomic) int age; // ... @endAnd its implementation:
// property implementation @implementation Cat @synthesize name, age; // ... @endWhich is much better. Properties can be called in two ways. Dot notation is the most terse and similar to many other languages:
// property dot notation example Cat *cat = [[Cat alloc] init]; NSLog(@"The cat is %d years old", cat.age); cat.name = @"Ritz";But you can use normal method calls as well:
// property method call example Cat *cat = [[Cat alloc] init]; NSLog(@"The cat is %d years old", [cat age]); [cat setName:@"Ritz"];These two examples are exactly equivalent. Dot notation is simply syntactic sugar for getter and setter method calls.
Which is which?
While it's very convenient, property dot notation makes Objective-C a little confusing at first. You will sometimes see properties and instance variables mixed together in the same method:
- (void)celebrateBirthday { age++; // instance variable if (age > 9) { self.name = // property [NSString stringWithFormat:@"Old %@", name]; // instance variable } }At first glance,
name
and self.name
don't seem that different but they are. We'll examine those differences, and look more at properties, next time.
6 comments:
I think this is the best article in the series so far. Thank you.
Thanks! It's good to know people are getting something out of them!
Thanks for this great article!
Thanks!
Thanks!
Explains things so clearly.
thanks a lot..
article is very good..
please explain each step after the program..
Post a Comment