Adding a Track Object
In this app, there are two custom classes that contain application logic. The Xcode template provided an AppDelegate
class and created an instance of it in the nib file for you. When you made connections between the UI elements and the app delegate, you added custom code to the AppDelegate
class. The app delegate acts as a controller object in the Model-View-Controller design pattern by updating views and managing model objects that store the actual application data. You now need to implement the model object that stores the track’s volume. You need to implement a Track
class and create an instance of it in the app delegate. When a UI element changes a value, the app delegate is responsible for updating the track’s volume and synchronizing the views.
Create Header and Implementation Files for the Track Class
The first task is to create header and implementation files for the new Track
class. It will inherit from the NSObject
class, the root class in Objective-C.
In Xcode, in the project navigator select the TrackMix group folder.
When you add new files in the next steps, they are added to the project navigator under this selection.
Create an Objective-C class as follows:
Choose File > New > File.
In the template dialog, select Cocoa in the OS X section.
Select the Objective-C class template and click Next.
In the screen that appears, set the class’s name to Track (by convention, class names begin with an upper case letter). Leave the new class as a subclass of the
NSObject
class, and click Next.In the Save dialog, leave the group and targets as they are, and click Create.
The files should now be located in your project’s TrackMix group folder. If you look at the files, you’ll see that stub class interface and implementation declarations are provided for you. You need to add an attribute to represent the volume.
Implement the Track Class
The track’s volume attribute is implemented using an Objective-C declared property. A declared property is a convenient way to implement an instance variable.
In the Track header file (
Track.h
), add a property declaration for afloat
value calledvolume
.The header file should look like this:
#import <Foundation/Foundation.h>
@interface Track : NSObject
@property (assign) float volume;
@end
When you build your app, the compiler reads the property declaration and generates a private instance variable and accessor methods for the volume
property. The value of the _volume
instance variable is automatically set to 0
when the Track
object is created.
Add a Track Property
For the app delegate to use an instance of Track
—to store the volume displayed by multiple UI elements—you add a track property to the AppDelegate
class.
The compiler will generate an error, though, if you declare a property without telling it about the Track
class. You could import the header file for the Track
class (Track.h
), but typically you provide a forward declaration using @class
. A forward declaration is a promise to the compiler that Track
will be defined somewhere else and that it needn’t waste time checking for it now. (Doing this also avoids circularities if two classes need to refer to each other and would otherwise include each other’s header files.) You then import the header file itself in the implementation file.
In the app delegate’s header file (
AppDelegate.h
), add a forward declaration for theTrack
class before the interface declaration forAppDelegate
.@class Track;
Add a declaration for the
track
property between the last@property
declaration and the@end
symbol.@property (strong) Track *track;
The property specifies the
strong
attribute rather than theweak
attribute, indicating that theTrack
object is owned by the app delegate. As long as the app delegate references it, theTrack
object won’t go away. The outlet property declarations you created earlier use theweak
attribute because the corresponding UI elements are contained in the view hierarchy that is owned by the window, not the app delegate. The window and its view hierarchy can go away independent of the app delegate.
Confirm that your AppDelegate
class interface file (AppDelegate.h
) looks like this. (The comments are not shown and the order of property and method declarations is not important.):
#import <Cocoa/Cocoa.h> |
@class Track; |
@interface AppDelegate : NSObject <NSApplicationDelegate> |
@property (assign) IBOutlet NSWindow *window; |
@property (weak) IBOutlet NSTextField *textField; |
@property (weak) IBOutlet NSSlider *slider; |
@property (strong) Track *track; |
- (IBAction)mute:(id)sender; |
- (IBAction)takeFloatValueForVolumeFrom:(id)sender; |
@end |
Create the Track Instance
Now that you’ve added the track
property to the AppDelegate
class, you need to set its value to an instance of Track
.
In the implementation file for the app delegate (
AppDelegate.m
), importTrack.h
by adding this line of code before the@implementation
statement:#import "Track.h"
Otherwise, you’ll see a compile error later when you reference the
Track
class.Create an instance of
Track
by adding the following code as the first two statements in the implementation of theapplicationDidFinishLaunching:
method:Track *aTrack = [[Track alloc] init];
[self setTrack:aTrack];
These two lines of code do the following:
Allocate memory for an instance of the
Track
class.Initialize the instance of the
Track
class to prepare it for use.Set the new track to be the
track
property using an accessor method.You don’t need to declare or write the code for
setTrack:
. The compiler automatically creates this accessor for thetrack
declared property that hides the_track
private instance variable.
Objective-C also supports dot notation. You can replace [self setTrack:aTrack];
with:
self.track = aTrack; |
The dot notation invokes exactly the same accessor method (setTrack:
) as in the original implementation. The dot notation provides a compact syntax for invoking accessor methods—especially when you use nested expressions.
Test the App
You can now test your app.
Compile and run the project (choose Build > Build and Run, or click the Run button in Xcode’s toolbar).
Your app should compile without errors, and you should see the same user interface as before. Disappointingly, the user interface should also behave as it did before. Although you now have a Track
object, the app delegate doesn’t do anything with it beyond creating it. There is a little more work to do to complete the implementation.
Recap
You implemented a model class called Track
to store a volume
attribute displayed by multiple UI elements. In the app delegate, you declared a property for a Track
object and performed a few other housekeeping tasks. Accessor methods for the property are automatically synthesized.
© 2013 Apple Inc. All Rights Reserved. (Last updated: 2013-04-23)