A new language usually requires a full semester. We will cover the Objective-C language in two and a half weeks.
Documentation:
j2objc
.
The language C has no expressions that start with the characters
[
,
^
,
or
@
,
and no declarations that start with
+
or
-
.
The language Objective-C is simply C with the addition of expressions
and declarations that start with these characters.
The Objective-C additions are therefore instantly recognizable
by their initial character.
A variable is a container. There are many types of variables. For example,
An
int
variable can hold an
integer
(whole number).
Each statement ends with a semicolon;
each comment starts with a double slash.
Case counts.
//Create an integer variable named i and store 10 into it. int i = 10;
//The name of a variable can be more than one letter. int width = 320; //means 320 pairs of pixels on iPhone 5S int height = 568;
//The above statements created new variables. //The following statement merely changes the value //of an existing variable. i = 3 + 4 * 5;
//Change the value of i to 35; //override the "precedence" of the operators. i = (3 + 4) * 5;
i = i + 1; //Add 1 to the variable. i now holds 36. i += 1; //Easier way to do the same thing. i now holds 37. ++i; //Even easier way (prefix increment). i now holds 38.
i++; //I discourage you from using postfix increment.
Is there any way an increment can fail?
The range of values for an
int
variable is centered (almost) around zero:
int biggest = 2147483647; // 231 - 1 int smallest = -2147483648; //-231That means that an
int
can hold any one of
We’ll need consecutively numbered integers to refer to the buttons on an alert view or action sheet. The Objective-C convention is to use embedded uppercase letters for compound words.
int abortButton = 0; int retryButton = 1; int ignoreButton = 2; int failButton = 3; int destructButton = 4;
A list of enumerations is an easy way to mass-produce a series of consecutively numbered integer variables. By default, the numbers start at zero. The following code does approximately the same thing as the above.
enum { abortButton, retryButton, ignoreButton, failButton, destructButton };
An
int
variable
can hold any one of
NSUInteger
variable can hold any one of
NS
stands for
NeXTSTEP,
software left over from
Steve Jobs’s
company
NeXT.
Follow the
NS
link
to see a typical page of
Apple’s documentation.
We’ll be reading it all semester.
int iBiggest = 2147483647; // 231 - 1 int iSmallest = -2147483648; //-231
NSUInteger uBiggest = 18446744073709551615; // 264 - 1 NSUInteger uSmallest = 0;
An
NSUInteger
can never hold a negative number.
An attempt to ram a negative number down its throat would result in
wrap around.
NSUInteger u = -1; //Store 18446744073709551615 into u. u = -2; //Store 18446744073709551614 into u. u = -3; //Store 18446744073709551613 into u.
NSUInteger
s
are used for counting things.
Early examples will be
the
count
property of an
NSSet
and the
count
property of an
NSArray
.
(Surprisingly, the
index
of an action sheet button is an
NSInteger
,
not an
NSUInteger
.
So are the
number of sections
in a
UITableView
,
and the
number
of rows in each section.)
A
BOOL
variable (after
George Boole)
is much more limited.
It can hold one of only two possible values,
YES
and
NO
.
Write them in all uppercase.
BOOL decision = YES; decision = NO; //Toggle the decision. Exclamation point means "the opposite of". decision = !decision;
Floats and doubles have room for a decimal point
and digits to the right of the decimal point.
CG
stands for Core Graphics.
int i = 3.14159; //Store 3 into i. CGFloat f = 3.14159; //A CGFloat can hold up to 6 significant digits. double d = 3.14159265358979; //A double can hold up to 15 significant digits.
To hold the location of a pixel on the screen,
a
CGFloat
will suffice.
We won’t need the 15 digits of a
double
to hold a number in the range 0 to 320.
Every variable has a memory address. We can get the address of the variable with an ampersand, chosen because the words “address” and “ampersand” begin with the same letter.
A
pointer
is a variable that can hold the address of another variable.
The most important use of a pointer will be to
point to (i.e., hold the address of)
an “object”.
But we haven’t seen any objects yet,
so we will demonstrate with a pointer that points to an
int
.
The following
pi
is a pointer of the specific type “pointer to
int
”.
We have to
declare
(create, and announce the name of) the pointer
with an asterisk.
int i = 10; int *pi = &i; //Store the address of i into pi. //We say that pi "points to" (i.e., holds the address of) i.
CGFloat f = 3.14159; CGFloat *pf = &f; //pf is a "pointer to CGFloat". //We say that pf "points to" (i.e., holds the address of) f.
Most of our strings will be Objective-C strings. But an SQLite database will reject an Objective-C string, so we will have to feed it a C string. A C string is a string (series) of characters stored in consecutive bytes of memory. A pointer will to a C string will actually be a pointer to the first character of the C string.
//p is a variable of type "pointer to char". //It points to the 'H' in the C string Hello. //The 'e' is in the byte of memory next to the 'H'. //The first 'l' is in the byte of memory next to the 'e'. Et cetera. char *p = "Hello";
For “strong” vs. “weak” pointers, see View Controller.
A big variable that contains little variables is called a structure. The little variables inside the structure are called the fields of the structure. Think of them as the structure’s internal organs.
CGPoint
and
CGSize
are two types of structure that happen to have the same number of fields
and the same data type for corresponding fields.
But the structures are used for totally different purposes.
The fields of a
CGPoint
might be negative, but the fields of a
CGSize
should never be negative.
(CG
stands for Core Graphics.)
CGPoint p = CGPointMake(0.0, 0.0); //The structure p holds the values 0.0 and 0.0 CGSize s = CGSizeMake(320.0, 568.0); //The structure s holds the values 320.0 and 568.0
The fields inside a
CGPoint
are named
x
and
y
.
Write the name of the structure variable to the left of the dot,
and the name of the field to the right.
CGPoint p = CGPointMake(0.0, 0.0); CGFloat x = p.x; CGFloat y = p.y;
The fields inside a
CGSize
are named
width
and
height
.
CGSize s = CGSizeMake(320.0, 568.0); CGFloat w = s.width; CGFloat h = s.height;
In the
location manager example,
the fields inside a
CLLocationCoordinate2D
structure will be named latitude and longitude.
CL
stands for Core Location.
A structure is not limited to containing only two fields.
A tailor might need a structure containing bust, waist, hips.
A weather report might need a structure containing
temperature, humidity, barometric pressure, and wind speed.
A structure can contain smaller structures as its fields.
For example,
a
CGRect
contains a
CGPoint
named
origin
and a
CGSize
named
size
.
CGRect rect = CGRectMake(0.0, 0.0, 320.0, 568.0); //x, y, width, height CGPoint p = rect.origin; //The fields inside of rect CGSize s = rect.size; //are named rect.origin and rect.size //An expression can have more than one dot, //like the expression 10+20+30 has more than one plus. CGFloat x = p.x; CGFloat y = rect.origin.y;
Here’s a diagram of the above structure
rect
and its contents.
In every language, a program may be divided into sections. In some languages, these sections are called subroutines. In other languages, they are called procedures. In this language, they are called functions.
Every function has a name;
examples are
sqrt
,
sin
,
cos
,
tan
.
Get used to sine and cosine if you want to do computer graphics.
To
call
(execute or invoke) a function,
write a pair of parentheses after
the function’s name.
//Call a function named f. f();
To send one or more arguments (pieces of information) to the function, write them in the parentheses.
//Call a function named f. Pass it one argument. f(10);The following example sends one argument to
NSLog
,
a function that will print debugging output in the Console window of Xcode.
The argument in this example is a
string
(series) of characters.
A string is always enclosed in
"
double quotes"
.
To make this string
immutable
(unchangeable),
i.e., an
NSString
as opposed to some other kind of string,
write an
@
in front of it.
Click on
this
link
to see that
NSLog
will accept only an
NSString
,
not any other kind of string.
//Call the function NSLog. Pass a string to it. NSLog(@"Arrived at the start of the main function.");
Multiple arguments are separated with commas.
//Call a function named f. Pass it three arguments. f(10, 20, 30); //Call a function named CGPointMake. Pass it two arguments. CGPoint p = CGPointMake(0.0, 0.0);
The function
NSLog
often takes multiple arguments.
The three most important
%
formats in its first argument are
%d
,
%g
,
and
%@
.
//Pass two arguments to NSLog. int i = 10; NSLog(@"The value of i is %d.", i); //d for int; it stands for "decimal"
CGFloat f = 3.14159; NSLog(@"The value of f is %g.", f); //g for CGFloat and double
NSString *s = @"This is a string."; NSLog(@"The value of s is %@.", s); //@ for NSString
But there are many more
%
formats;
see the following example and the
complete
list.
CHAR_BIT
is the number of bits per byte.
sizeof
gives us the number of bytes in a variable.
Their product is the number of bits in the variable.
The number of bits might depend on whether you have a 32- or 64-bit device.
int i = 10; NSLog(@"The value of i is %d.", i); //d for decimal NSLog(@"The address of i is %p.", &i); //p for pointer NSLog(@"The number of bytes in i is %zu.", sizeof i); //z for size_t, u for unsigned NSLog(@"The number of bits in i is %zu.", CHAR_BIT * sizeof i);
2014-02-04 16:11:05.444 Project[47920:a0b] The value of i is 10. 2014-02-04 16:11:05.446 Project[47920:a0b] The address of i is 0x7fff5fbfed0c. 2014-02-04 16:11:05.446 Project[47920:a0b] The number of bytes in i is 4. 2014-02-04 16:11:05.447 Project[47920:a0b] The number of bits in i is 32.
If you select iPhone Retina (4-inch 64-bit) in the upper left corner of Xcode,
your
CGFloat
will occupy 64 bits of memory.
CGFloat f = 3.14159; NSLog(@"The value of f is %g.", f); //g for CGFloat and double NSLog(@"The address of f is %p.", &f); //p for pointer NSLog(@"The number of bytes in f is %zu.", sizeof f); //z for sizze_t, u for unsigned NSLog(@"The number of bits in f is %zu.", CHAR_BIT * sizeof f);
2014-02-04 16:13:03.777 Hello[50841:a0b] The value of f is 3.14159. 2014-02-04 16:13:03.779 Hello[50841:a0b] The address of f is 0x7fff5fbfed08. 2014-02-04 16:13:03.779 Hello[50841:a0b] The number of bytes in f is 8. 2014-02-04 16:13:03.780 Hello[50841:a0b] The number of bits in f is 64.
//Pass three arguments to NSLog. CGPoint p = CGPointMake(0.0, 0.0); NSLog(@"p == (%g, %g)", p.x, p.y); //g for CGFloat and double
CGSize s = CGSizeMake(320.0, 568.0); NSLog(@"s == %g × %g", s.width, s.height);
//Pass five arguments to NSLog. CGRect r = CGRectMake(0.0, 0.0, 320.0, 568.0); NSLog(@"r == (%g, %g), %g × %g", r.origin.x, r.origin.y, r.size.width, r.size.height );
Some functions give us a return value (answer or result). We can store the return value into a variable.
//Call the sqrt function. Store its return value (1.414213562373115) into s. double s = sqrt(2.0); NSLog(@"The square root of 2.0 is %g.", sqrt(2.0));
We have already seen several functions with return values:
CGPoint p = CGPointMake(0.0, 0.0); CGSize s = CGSizeMake(320.0, 568.0); CGRect rect = CGRectMake(0.0, 0.0, 320.0, 568.0);
The first ones we will see under actual combat conditions will be
NSStringFromClass
and
UIApplicationMain
.
return UIApplicationMain(argc, argv, nil, NSStringFromClass([ProjectAppDelegate class]));
An
object
is a thing to which you can send a
message.
To send a message to an object,
write the object on the left,
the message on the right,
and enclose them in
[
square brackets]
.
The object is said to be the
receiver
of the message.
Only an object can receive a message.
The variables we have seen so
far—int
,
NSUInteger
,
BOOL
,
CGFloat
,
double
,
structures, pointers—cannot receive messages.
[spartans prepareForGlory]; //300 (2007) [luke useTheForce]; //Star Wars (1977) [hal openThePodBayDoors]; //2001: A Space Odyssey (1968) [gort klaatuBaradaNikto]; //The Day the Earth Stood Still (1951) [rick hideMe]; //Casablanca (1942) [dorothy surrender]; //The Wizard of Oz (1939)
[super init]; //the first real example we'll see in Objective-C
There are many
classes
(types) of objects.
The name of a class always begins with an uppercase letter.
For example,
let’s temporarily assume that
rick
is an object of class
BogieCharacter
.
There’s another way in which an object is different from an
integer,
BOOL
,
CGFloat
,
or even a structure.
Each of these little things can be stored in a variable.
But an object is too big to be stored in a variable.
Only the
address
of the object can be stored in a variable.
The
“object”
rick
,
for example,
is not an object at all.
It is merely a variable that holds the address of an object.
The object is the thing that is of class
BogieCharacter
.
rick
is merely a variable of type
“pointer to
BogieCharacter
”,
analogous to the variable of type “pointer to
int
”
we saw above.
Since
rick
is a pointer,
it must have been declared with the asterisk shown below.
We ask you to accept on faith that the value of the expression
[[BogieCharacter alloc] init]
BogieCharacter
.
int i = 10; int *p = &i; BogieCharacter *rick = [[BogieCharacter alloc] init];
A variable of type
“pointer to
BogieCharacter
”
can hold only one type of thing:
the address of an object of class
BogieCharacter
.
But a variable of type
id
is flexible enough to hold the address of an object of any class.
An
id
is a pointer,
even though it is declared without an asterisk.
id p = rick; //Store the address of a BogieCharacter into p. p = dorothy; //Store the address of a GarlandCharacter into p. p = signorUgarte; //Store the address of a PeterLorreCharacter into p.
I don’t deny there is a remarkable quantity of ivory—mostly fossil. We must save it, at all events—but look how precarious the position is—and why? Because the method is unsound. | ||
— Joseph Conrad, Heart of Darkness, Chapter III |
An object can have functions.
The functions that belong to an object are called the
methods
of that object.
Most of the functions that we call will be methods.
Only occasionally will we call a function such as
sqrt
,
which is an orphan that belongs to no object.
When we send a message to an object,
we are really calling the method that has the same name.
For example, when we send the message
hideMe
to the object pointed to by
rick
,
we are really calling the method
hideMe
that belongs to that object.
(From now on, we will refer to this object as
rick
,
even though
rick
is really the name of the pointer, not of the object.
The object actually has no name.)
double s = sqrt(2.0);Similarly, when we call a function that is a method, we can get back a return value. For example, when we call the
bounds
method
of the
screen
object
s
,
we get back a return value which is a structure of type
CGRect
.
We store the structure in the variable
b
of type
CGRect
.
(We ask you to accept on faith that
[UIScreen mainScreen]
UIScreen
.)
//Let s be a pointer to the main screen of the app. UIScreen *s = [UIScreen mainScreen]; //Get the location and size of the screen. CGRect b = [s bounds];
Incidentally,
because
bounds
is a
“property”
as well as a method,
there’s a shortcut.
A property of an object can be accessed with the same
dot
with which we accessed a field of a structure.
//Get the location and size of the screen. CGRect b = s.bounds; //easier way to do the same thing
We saw that an argument passed to a function is written in parentheses.
double s = sqrt(2.0);
An argument passed to a method is written after the name of the method, preceded by a colon. The colon is actually part of the name of the method.
[rick hide: me]; //the name of the method is hide: [rick hide: myChips]; [rick hide: theLettersOfTransit]; [luke use: theForce]; [hal open: thePodBayDoors]; [myObject gotoX: 10]; //the name of the method is gotoX:
Ilsa:
How nice, you remembered.
But of course, that was the day the Germans marched into Paris.
Rick: Not an easy day to forget. Ilsa: No. Rick: I remember every detail. The Germans wore gray, you wore blue. |
||
—Casablanca (1942) |
We can send more than one argument to a method.
The following method is named
gotoX:y:
[myObject gotoX: 10.0 y: 20.0];
The method named
gotoX:y:z:
[myObject gotoX: 10.0 y: 20.0 z: 30.0];
Other methods that take two arguments:
[ilsa theGermansWore: gray youWore: blue]; [captainRenault andTheNamesAre: mr and: mrsVictorLaszlo]; [strasser iWasWillingToShoot: captainRenault andImWillingToShoot: you];
If the method takes a variable number of arguments,
separate them with commas.
The method
stringWithFormat:
,
for example, takes a first arguments that is a
printf
-style
format string
like the first argument of
NSLog
.
int month = 12; int day = 31; int year = 2014; int hour = 18; int minute = 30; int second = 0; //Four arguments NSString *s = [NSString stringWithFormat: @"%d/%d/%d", month, day, year]; //Seven arguments NSString *t = [NSString stringWithFormat: @"%d/%d/%d %02d:%02d:%02d", month, day, year, hour, minute, second]; //v = "Today's date is 12/31/2014." NSString *u = @"Today's date is "; NSString *v = [u stringByAppendingFormat: @"%d/%d/%d.", month, day, year];
Other early examples of methods with a variable number of arguments
will be the
setWithObjects:
method of class
NSSet
(see
here),
and the
arrayWithObjects:
method of class
NSArray
(see
here).
Here’s a typical use of a typical method of a typical object.
[myObject myMethod: myArgument];
The name of a method can be stored in a string object. Remember that the colons are part of the name.
//An NSString object can hold any string (sequence) of characters. NSString *string = @"myMethod:";The name of a method can also be stored in a special type of variable called a selector. It holds the same information, but in a form that can be used for a special purpose, shown below.
//A selector can hold only a string that is the name of a method. SEL sel = @selector(myMethod:);
To wait 10 seconds and then do
[myObject myMethod: myArgument];
,
SEL sel = @selector(myMethod:); [myObject performSelector: sel withObject: myArgument afterDelay: 10];
The two statements above can be telescoped to a single statement:
[myObject performSelector: @selector(myMethod:) withObject: myArgument afterDelay: 10];
A selector in Objective-C does the same job as a “pointer to member function” in the language C++. We will pass a selector to
performSelector:withObject:afterDelay:
method of class
NSObject
when we detect the difference between a single and double
tap.addTarget:action:forControlEvents:
method of class
UIControl
(What message should the
button
send to which target object when the button is pressed?)
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
method of class
NSTimer
addObserver:selector:name:object:
method of class
NSNotificationCenter
Another way to pass a chunk of code to a method is as a block, often used with animations. See A Short Practical Guide to Blocks.
We have already seen parentheses in an expression. Here are nested parentheses:
int result = ((1 + 2) * 3 + 4) * 5; //The result is 65.We can avoid the nesting, and even avoid the parentheses altogether, by introducing extra variables.
int i = 1 + 2; int j = i * 3; int k = j + 4; int result = k * 5;
s
)
we get back from one method
might be the receiver to which the next message is sent.
UIScreen *s = [UIScreen mainScreen]; CGRect b = [s bounds];We can telescope this to
CGRect b = [[UIScreen mainScreen] bounds];with nested square brackets.
b
)
we get back from one method
might also be passed as an argument to the next method:
CGRect b = [s bounds]; [window initWithFrame: b];We can telescope this to
[window initWithFrame: [s bounds]];with nested square brackets.
UIScreen *s = [UIScreen mainScreen]; CGRect b = [s bounds]; [window initWithFrame: b];can be telescoped to a single statement:
[window initWithFrame: [[UIScreen mainScreen] bounds]];
The classes that have already been written for us begin with a pair of initials. Some example are
NS
(NeXTSTEP),
e.g.,
NSAutoreleasePool
,
NSString
.
UI
(User Interface),
e.g.
UIColor
,
UIFont
,
UIScreen
,
UIView
,
UIWindow
.
CG
(Core Graphics)
CL
(Core Location)
Most messages are sent to objects,
but we can also send a message to a class.
Think of the class as a factory that manufactures objects of that class.
Let’s say we have a class named
Point
.
(The name of a class begins with an uppercase letter;
the name of a class invented by Apple begins with two uppercase letters.
This class was obviously not created by Apple.)
The
alloc
message tells the class to
instantiate
(create) a new object of that class.
The new object is called an
instantiation
of the class.
alloc
returns the address of the instantiation
(i.e., the address of the newborn object).
We store the address in a pointer named
point
.
Point *point = [Point alloc];
An object created by
alloc
does not become operational until we also send it the message
init
.
Point *point = [Point alloc]; //Send a message to a class. [point init]; //Send a message to an object.
Since
init
returns the address of the object to which it belongs,
we can telescope these statements as follows.
Point *point = [[Point alloc] init];with nested square brackets. This is an example of the common idiom
Classname *c = [[Classname alloc] init];
The
init
method that we just called to create a Point
object
takes no arguments.
The
init
method that we call to create an object of another class
might take several arguments;
an example is the above
initWithFrame
.
Point *point = [[Point alloc] init]; Point1D *point1d = [[Point1D alloc] initWithX: 10]; Point2D *point2d = [[Point2D alloc] initWithX: 10 y: 20]; Point3D *point3d = [[Point3D alloc] initWithX: 10 y: 20 z: 30]; CGRect frame = CGRectMake(0.0, 0.0, 320.0, 568.0); UIWindow *window = [[UIWindow alloc] initWithFrame: frame];
Instantiating an object usually requires calls to two methods:
the
alloc
method of a class,
followed by the
init
method of the newborn object.
For example, an object of class
NSNumber
holds one number.
NSNumber *n = [[NSNumber alloc] initWithInt: 10]; //Instantiate a new NSNumber object. int i = [n intValue]; //Store 10 into i.But some classes let us instantiate an object by calling a single method of the class. This method is referred to as a convenience constructor. Let’s instantiate the same object with a convenience constructor.
NSNumber *n = [NSNumber numberWithInt: 10]; //Instantiate a new NSNumber object. int i = [n intValue]; //Store 10 into i.
Similarly, an object of class
NSValue
can hold one
CGPoint
structure.
CGPoint p = CGPointMake(10.0, 20.0); NSValue *v = [NSValue valueWithCGPoint: p]; //Instantiate a new NSValue object. CGPoint q = [v CGPointValue]; //Store the point (10.0, 20.0) into q.
To make sure that a pointer does not point to any object,
put the special value
nil
into the pointer.
The following pointer is declared to be an
id
,
so it could potentially point at any object of any class.
But its initial value is
nil
,
so it definitely does not point to any object.
id p = nil;
The
init
method,
its variants with arguments (such as
initWithFrame:
),
and the convenience constructors
return
nil
if they were unable to instantiate an object.
If this is a real possibility,
the conscientious programmer should
follow each attempt at instantiation
with an
if
statement.
Point1D *point1d = [[Point1D alloc] initWithX: 10]; if (point1d == nil) { //two equal signs mean comparison NSLog(@"unable to create point1d"); }The above
if
is usually abbreviated
if (!point1d) { //Exclamation point means "not". NSLog(@"unable to create point1d"); }
A check for a successful instantiation would look like this
if (point1d != nil) { //!= means not equals NSLog(@"was able to create point1d"); }and is usually abbreviated
if (point1d) { NSLog(@"was able to create point1d"); }
There is only one object of class
UIScreen
.
This object exists eternally and is never created or destroyed,
so we would never attempt to instantiate it.
Instead,
we can get the address of the one-and-only screen object by calling the method
mainScreen
of class
UIScreen
.
UIScreen *screen = [UIScreen mainScreen];
Similarly, there is only one object of the following classes.
UIApplication *application = [UIApplication sharedApplication]; UIDevice *device = [UIDevice currentDevice]; AVAudioSession *session = [AVAudioSession sharedInstance]; UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
A method that belongs to an object,
and that is called when a message is sent to the object,
is said to be an
instance method
of that object.
Examples are the
init
method of every object,
and the
drawRect:
method of every object of class
UIView
.
A method that belongs to a class,
and that is called when a message is sent to the class,
is said to be a
class method
of that class.
Examples are the
alloc
method of every class,
the
mainScreen
method of class
UIScreen
,
and the convenience constructors such as the
numberWithInt:
method of class
NSNumber
.
When creating a method,
we write a
+
in front of a class method and a
-
in front of an instance method.
A big structure can contain little variables inside it, called the fields of the structure. Similarly, a big object can contain little variables inside it, called the instance variables of the object.
An instance variable of an object can usually be mentioned
only by the methods of that object.
That’s why we think of the instance variables
as the “internal organs” of the object.
An instance variable that can also be mentioned elsewhere is called a
property
of the object;
the example we have seen is the
bounds
property of every object of class
UIView
.
We will turn an instance variable into a property
by creating a pair of methods that
read and write the instance variable.
The simplest way to create this pair of methods is with the
@property
and
@synthesize
statements.
A
class
is the blueprint for a certain type of object.
The class lists all the instance variables, properties, and methods
that would belong to any object of this type.
A class named
Person
,
for example, might say that any object of type
Person
would contain a last name, first name, social security number, etc.
The class would also say that any object of type
Person
has methods named
hire
,
fire
,
etc.
This would let us send the messages
hire
or
fire
to any object of this class.
It is possible to create a class from scratch.
Usually,
however,
we build a class by starting with an existing class
and adding extra instance variables, properties, and methods to it.
Let’s start with the existing class
NSString
.
An object of this class is capable of holding any
string of characters: a word, several words, etc.
Here’s an example:
NSString *s = @"hello"; NSLog("@s contains %@.", s);
The bigger and better class
NSMutableString
was built by starting with class
NSString
and adding extra instance variables, properties, and methods.
Class
NSMutableString
has all the instance variables, properties, and methods that class
NSString
has, plus more.
This means that an object of class
NSMutableString
has all the instance variables, properties, and methods of an object of class
NSString
, plus more.
The additional functionality delivered by an
NSMutableString
object is the ability to change the string that is stored in the object.
Every
NSMutableString
is a
NSString
,
but not every
NSString
is a
NSMutableString
.
We say that class
NSString
is the
superclass
and class
NSMutableString
is the
subclass,
and that the subclass is
derived from
the superclass.
The documentation for each class always tells who the superclass is
for that class,
if there is a superclass.
In a diagram showing the relationship between classes,
the superclass is always drawn above its subclass(es).
Class
NSString
in turn was derived from class
NSObject
,
so class
NSMutableString
is really a grandchild.
All the classes belong to one big family.
The above diagram showed the relationship between three classes.
Now let’s show where the objects of each class are located in memory.
An object of class
NSMutableString
contains an object of class
NSString
inside of it.
The
NSString
object is called the
superclass
object of the
NSMutableString
object.
(The
NSMutableString
object will humbly refer to itself as
“self
”,
and will respectfully refer to the
NSString
object inside it as
“super
”.
Those of you who read that notorious self-help book for men,
Robert Bly’s
Iron John,
with its instructions for drumming in the forest and getting in touch with your
“inner warrior”,
will find this territory familiar.)
The innermost object is always created first, then the middle object, and then the outer object. (If the outer object was created first, then for a brief period it would be hollow. But we never want to have a hollow object: it could not survive for even a millisecond with no internal organs.) Conversely, when the whole thing is torn down, the outermost object is destroyed first, then the middle object, and then the inner one.
Accountants call this sequence “LIFO”:
last in, first out.
The rest of us call it
“last hired, first fired”.
Now let’s translate it into Objective-C.
The act that brings an object to life is the call to its
init
method.
Memorize the following incantations.
The
init
method of the subclass always
begins
by calling the
init
method of the superclass.
The dealloc
method of the subclass always
ends
by calling the
dealloc
method of the superclass.
|
There are many examples of inheritance,
all starting with class
NSObject
.
Inheritance allows
software to be built in layers.
NSObject NSString (has everything an NSObject has, plus the ability to hold a string) NSMutableString (has everything an NSString has, plus the ability to change the string)
NSObject NSArray NSMutableArray
NSObject NSSet NSMutableSet
NSObject NSDictionary NSMutableDictionary
NSObject UIResponder (has everything an NSObject has, plus the ability to respond to touches and G-forces) UIView (has everything a UIResponder has, plus the ability to appear on the screen) UIWindow (has everything a UIView has, plus the ability to be the root view)
NSObject Shape (the textbook example) Square, Triangle, Circle, etc.: a class can have more than one subclass
NSObject Amniote (has an amnion) Synapsid (has everything an Amniote has, plus a synapsid opening) Mammal (has everything a Synapsid has, plus those 3 little bones in the middle ear) Placental (has everything a Mammal has, plus a placenta)
A class can inherit more than just instance variables, properties, and methods.
It can also inherit
protocols.
A protocol is a set of obligations.
Class
HelloAppDelegate
in the
Hello
app,
for example,
will be derived from
NSObject <UIApplicationDelegate>
.HelloAppDelegate
inherits
the instance variables, properties, and methods of
NSObject
,
and also inherits the set of obligations named
UIApplicationDelegate
.
One of the obligations imposed by
UIApplicationDelegate
is to have a method named
application:didFinishLaunchingWithOptions:
applicationDidFinishLaunching:
applicationWillTerminate:
.
An Objective-C protocol is similar to a Java “interface”.