In
the preceding hour, you learned how to use Xcode to create projects and
navigate their files. As mentioned then, the vast majority of your time
will be spent in the Classes folder of Xcode, shown in Figure 1.
You’ll be adding methods to class files that Xcode creates for you when
you start a project or, occasionally, creating your own class files to
implement entirely new functionality in your application.
Okay, sounds simple enough,
but where will the coding take place? If you create a project and look
in the Classes folder, you’ll see quite a few different files staring
back at you.
Header/Interface Files
Creating a class creates
two different files: an interface (or header) file (.h) and an
implementation file (.m). The interface file is used to define a list
of all of the methods and properties that your class will be using.
This is useful for other pieces of code, including Interface Builder to determine how to access information and features in your class.
The implementation file, on
the other hand, is where you’ll go to write the code that makes
everything defined in the header file work. Let’s review the structure
of the very short, and entirely made-up, interface file in Listing 1.
Listing 1.
1: #import 2: 3: @interface myClass : myParent { 4: NSString *myString; 5: IBOutlet UILabel *myLabel; 6: } 7: 8: +(NSString)myClassMethod:(NSString)aString; 9: 10: -(NSDate)myInstanceMethod:(NSString)aString anotherParameter:(NSURL)aURL; 11: 12: @property (nonatomic, retain) UILabel *myLabel; 13: 14: @end
|
The #import Directive
First, in line 1, the interface file uses the #import directive to include any other interface files that our application will need to access. The string designates the specific file (in this case, UIKit,
which gives us access to a vast majority of the classes). If we need to
import a file, we’ll be explaining how and why in the text. The UIKit
example will be included by default when Xcode sets up your classes and
covers most of what you’ll need for this book’s examples.
Directives
are commands that are added to your files that help Xcode and its
associated tools build your application. They don’t implement the logic
that makes your app work, but they are necessary for providing
information on how your applications are structured so that Xcode knows
how to deal with them.
|
The @interface Directive and Instance Variables
Line 3 uses the @interface
directive to begin a set of lines (enclosed in {} braces) to describe
all the instance variables that your class will be providing:
3: @interface myClass : myParent {
4: NSString *myString;
5: IBOutlet UILabel *myLabel;
6: }
In this example, a variable that contains an object of type NSString named myString is declared, along with an object of type UILabel that will be referenced by the variable myLabel. An additional keyword IBOutlet is added to the front of the UILabel declaration to indicate that this is an object that will be defined in Interface Builder.
Watch Out!
All instance variables, method declaration lines, and property declarations must end with a semicolon (;).
Notice that line 3 includes a few additional items after the @interface directive: myClass : myParent .
The first of these is the name that we’re giving the class that we’re
working on. Here, we’ve decided the class will be called myClass.
The class name is then followed by a colon (:) and a list of the
classes that this class is inheriting from (that is, the “parent”
classes). Finally, the parent classes are followed by a list of
“protocols” enclosed within angle brackets, <>.
By the Way
The implementation and
interface files for a class will usually share the name of the class.
Here, the interface file would be named myClass.h and the
implementation file myClass.m.
Protocols are a unique
feature of Objective-C that sound complicated, but really aren’t.
Sometimes you will come across features that require you to write
methods to support their use—such as providing a list of items to be
displayed in a table. The methods that you need to write are grouped
together under a common name—this is known as a “protocol.”
Some protocol methods are
required, others are optional—it just depends on the features you need.
A class that implements a protocol is said to “conform” to that protocol.
|
Defining Methods
Lines 8 and 10 declare two methods that need to be implemented in the class:
8: +(NSString)myClassMethod:(NSString)aString;
9:
10: -(NSDate)myInstanceMethod:(NSString)aString anotherParameter:(NSURL)aURL;
Method declarations follow a simple
structure. They begin with a + or -; the + denotes a class method,
whereas - indicates an instance method. Next, the type of information
the method returns is provided in parentheses, followed by the name of
the method itself. If the method takes a parameter, the name is
followed by a colon, the type of information the method is expecting,
and the variable name that the method will use to refer to that
information. If multiple parameters are needed, a short descriptive
label is added, followed by another colon, data type, and variable
name. This pattern can repeat for as many parameters as needed.
In the example file, line 8 defines a class method named myClassMethod that returns an NSString object and accepts an NSString object as a parameter. The input parameter is made available in a variable called aString.
Line 10 defines an instance method named myInstanceMethod that returns an NSDate object, also takes an NSString as a parameter, and includes a second parameter of the type NSURL that will be available to the method via the variable aURL.
Did You Know?
Very frequently you will see methods that accept or return objects of the type id.
This is a special type in Objective-C that can reference any kind of
object and proves useful if you don’t know exactly what you’ll be
passing to a method, or if you want to be able to return different
types of objects from a single method.
Another popular return type for methods is void. When you see void used, it means that the method returns nothing.
The @property Directive
The final functional piece of the interface file is the addition of @property directives, demonstrated in line 12:
12: @property (nonatomic, retain) UILabel *myLabel;
The @property
directive is used in conjunction with another command called synthesize
in the implementation file to simplify how you interact with the
instance variables that you’ve defined in your interface.
Traditionally, to interact with the objects in your instance variables, you have to use methods called getters and setters
(or accessors and mutators, if you want to sound a bit more exotic).
These methods, as their names suggest, get and set values in your
instance variable objects. For example, a UILabel object, like what we’re referencing with the myLabel
instance variable in line 12, represents an onscreen text label that a
user can see. The object, internally, has a variety of instance
variables itself, such as color, font, and the text that is displayed.
To set the text, you might write something like this:
[myLabel setText:@"Hello World"];
And to retrieve the text currently displayed, you’ use the following:
theCurrentLabel=[myLabel getText];
Not too tough, but it’s not as easy as it could be. If we use @property and synthesize to define these as properties, we can simplify the code so that it looks like this:
myLabel.text=@"Hello World";
theCurrentLabel=myLabel.text;
We’ll make use of this
feature nearly everywhere that we need easy access to instance
variables. After we’ve given this treatment to an instance variable, we
can refer to it as a property. Because of this, you’ll typically see things referred to as “properties” rather than instance variables.
Did You Know?
The attributes (nonatomic, retain) that are provided to the @property directive tell Xcode how to treat the property it creates. The first, nonatomic,
informs the system that it doesn’t need to worry about different parts
of the application using the property at the same time, whereas retain
makes sure that the object the property refers to will be kept around.
These are the attributes you should use in nearly all circumstances, so
get used to typing them!
Ending the Interface File
To end the interface file, add @end on its own line. This can be seen on line 14 of our example file:
That’s it for the interface!
Although that might seem like quite a bit to digest, it covers almost
everything you’ll see in an interface/header file. Now let’s look at
the file where the actual work gets done: the implementation file.
Implementation Files
After you’ve
defined your instance variables (or properties!) and methods in your
interface file, you need to do the work of writing code to implement
the logic of your application. The implementation file (.m) holds all
of the “stuff” that makes your class work. Let’s take a look at Listing 3.2, a sample skeleton file myClass.m that corresponds to the interface file we’ve been reviewing.
Listing 2.
1: #import "myClass.h" 2: 3: @implementation myClass 4: 5: @synthesize myLabel; 6: 7: +(NSString)myClassMethod:(NSString)aString { 8: // Implement the Class Method Here! 9: } 10: 11: -(NSString)myInstanceMethod:(NSString)aString anotherParameter:(NSURL)aURL { 12: // Implement the Instance Method Here! 13: } 14: 15: @end
|
The #import Directive
The #import directive kicks things off in line 1 by importing the interface file associated with the class:
When you create your
projects and classes in Xcode, this will automatically be added to the
code for you. If any additional interface files need to be imported,
you should add them to the top of your interface file rather than here.
The @implementation Directive
The implementation
directive, shown in line 3, tells Xcode what class the file is going to
be implementing. In this case, the file should contain the code to
implement myClass:
3: @implementation myClass
The @synthesize Directive
In line 5, we use the @synthesize directive to, behind the scenes, generate the code for the getters and setters of an instance variable:
Used along with the @property
directive, this ensures that we have a straightforward way to access
and modify the contents of our instance variables as described earlier.
Method Implementation
To provide an area
to write your code, the implementation file must restate the method
definitions, but, rather than ending them with a semicolon (;), a set
of curly braces, {}, is added at the end, as shown in lines 7–9 and
11–13. All the magic of your programming will take place between these
braces:
7: +(NSString)myClassMethod:(NSString)aString {
8: // Implement the Class Method Here!
9: }
10:
11: -(NSString)myInstanceMethod:(NSString)aString anotherParameter:(NSURL)aURL {
12: // Implement the Instance Method Here!
13: }
By the Way
You can add a text comment on any line within your class files by prefixing the line with the // characters. If you’d like to create a comment that spans multiple lines, you can begin the comment with the characters /* and end with */.
Ending the Interface File
To end the implementation file, add @end on its own line just like the interface file. This can be seen on line 15 of our example:
Structure for Free
Even though we’ve just
spent quite a bit of time going through the structure of the interface
and implementation files, you’re rarely (if ever) going to need to type
it all out by hand. Whenever you add a new class to your Xcode project,
the structure of the file will be set up for you. Of course, you’ll
still need to define your variables and methods, but the @interface and @implementation directives and overall file structure will be in place before you write a single line of code.