Intro to Object-Oriented Design: Part 1/2

This tutorial series will teach you the basics of object-oriented design. In this first part: Inheritance, and the Model-View-Controller pattern. By Ellen Shapiro.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 5 of this article. Click here to view the first page.

Overriding Methods

Now that you’ve added the appropriate additional properties, you can add one new method and override several methods from the superclass to provide full implementations of those methods.

Overriding a method means “taking a method declared in the superclass and creating your own implementation.” 

For example, when you add a new UIViewController object, it already comes with overridden methods for initWithNibName:bundle:, viewDidLoad, and didReceiveMemoryWarning.

When you override a method, you can do one of two things:

  1. Include a call to the [super method] to take advantage of everything happening higher up the inheritance chain, or
  2. Provide your own implementation from scratch.

In all of the UIViewController methods, you can tell that Apple wants you to call the [super method] - there’s some important stuff in there that needs to execute before your UIViewController subclass can do its work.

However, since most of the methods you're going to override in the Car class are returning nil, you can just create your own implementations. There's nothing useful in the superclass's implementation so there's no need to call it.

Open Car.m and add the following private method to simplify your superclass override:

#pragma mark - Private method implementations
- (NSString *)start
{
    return [NSString stringWithFormat:@"Start power source %@.", self.powerSource];
}

Some vehicles such as bicycles don't need to be started, but cars do! In this case, you're not publicly declaring start since it should only be called within the implementation.

Note: Even though a method is "private" and other objects and classes can't see it, that doesn't protect a subclass from overriding the method. You really can't stop something from going wrong in this case, but you should make notes in your app's documentation, just as Apple does.

Next, add the remaining superclass overrides:

#pragma mark - Superclass Overrides
- (NSString *)goForward
{
    return [NSString stringWithFormat:@"%@ %@ Then depress gas pedal.", [self start], [self changeGears:@"Forward"]];
}

- (NSString *)goBackward
{
    return [NSString stringWithFormat:@"%@ %@ Check your rear view mirror. Then depress gas pedal.", [self start], [self changeGears:@"Reverse"]];
}

- (NSString *)stopMoving
{
    return [NSString stringWithFormat:@"Depress brake pedal. %@", [self changeGears:@"Park"]];
}

- (NSString *)makeNoise
{
    return @"Beep beep!";
}

Now that you have a concrete, or fully implemented, subclass of Vehicle, you can start building out your Table View controller.

Building out the User Interface

In VehicleListTableViewController.m, add the following import to the top of the file, just below the import for Vehicle:

#import "Car.h"

Next, add the following method between didReceiveMemoryWarning and #pragma mark - Table View:

#pragma mark - Data setup
-(void)setupVehicleArray
{
    //Create a car.
    Car *mustang = [[Car alloc] init];
    mustang.brandName = @"Ford";
    mustang.modelName = @"Mustang";
    mustang.modelYear = 1968;
    mustang.isConvertible = YES;
    mustang.isHatchback = NO;
    mustang.hasSunroof = NO;
    mustang.numberOfDoors = 2;
    mustang.powerSource = @"gas engine";
    
    //Add it to the array
    [self.vehicles addObject:mustang];
    
    //Create another car.
    Car *outback = [[Car alloc] init];
    outback.brandName = @"Subaru";
    outback.modelName = @"Outback";
    outback.modelYear = 1999;
    outback.isConvertible = NO;
    outback.isHatchback = YES;
    outback.hasSunroof = NO;
    outback.numberOfDoors = 5;
    outback.powerSource = @"gas engine";
    
    //Add it to the array.
    [self.vehicles addObject:outback];
    
    //Create another car
    Car *prius = [[Car alloc] init];
    prius.brandName = @"Toyota";
    prius.modelName = @"Prius";
    prius.modelYear = 2002;
    prius.hasSunroof = YES;
    prius.isConvertible = NO;
    prius.isHatchback = YES;
    prius.numberOfDoors = 4;
    prius.powerSource = @"hybrid engine";
    
    //Add it to the array.
    [self.vehicles addObject:prius];

    //Sort the array by the model year
    NSSortDescriptor *modelYear = [NSSortDescriptor sortDescriptorWithKey:@"modelYear" ascending:YES];
    [self.vehicles sortUsingDescriptors:@[modelYear]];
}

This simply separates the data setup methods and adds the method to construct your vehicle array.

Find awakeFromNib and add this code to the end of the method:

  // Initialize the vehicle array
  self.vehicles = [NSMutableArray array];

  // Call the setup method
  [self setupVehicleArray];

  // Set the title of the View Controller, which will display in the Navigation bar.
  self.title = @"Vehicles";

The above method executes when your Storyboard finishes constructing the nib for a particular UIViewController at runtime. It calls the setupVehicleArray method you just created, and sets the title of the VehicleListTableViewController to reflect its contents.

Build and run your application; you’ll see something that looks like the following:

Three Cars

The numbers you see represent memory addresses and will be different, but everything else should look the same.

The good news is that these objects are being recognized as Car objects. The bad news is that what’s being displayed isn’t terribly useful. Take a look at what’s set up in the UITableViewDataSource method tableView:cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    Vehicle *rowVehicle = self.vehicles[indexPath.row];
    cell.textLabel.text = [rowVehicle description];
    return cell;
}

Here, you’re grabbing a UITableViewCell object then getting the Vehicle at the index in the self.vehicles array matching the row of the cell you're constructing. Next, you set that specific Vehicle’s description string as the text for the cell’s textLabel.

The string produced by the description method (which is inherited from NSObject) isn’t very human-friendly. You’ll want to define a method in Vehicle that describes what each Vehicle object represents in a way that is easy for your users to understand.

Go back to Vehicle.h and add the following new method declaration, below all the other basic method declarations, but above @end:

//Convenience method for UITableViewCells and UINavigationBar titles.
-(NSString *)vehicleTitleString;

Then, in Vehicle.m, add the following implementation, again below the basic method implementations:

#pragma mark - Convenience Methods
-(NSString *)vehicleTitleString
{
    return [NSString stringWithFormat:@"%d %@ %@", self.modelYear, self.brandName, self.modelName];
}

The method above takes three properties that should be used on every single Vehicle and uses them to describe the vehicle concisely.

Now, update VehicleListTableViewController's tableView:cellForRowAtIndexPath: method to use this new method on Vehicle as follows:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    Vehicle *rowVehicle = self.vehicles[indexPath.row];
    cell.textLabel.text = [rowVehicle vehicleTitleString];
    return cell;
}

Build and run your application; it should look a little nicer now:

Three Vehicles With Readable Titles

However, if you select a Vehicle from the list, all you'll see are the same elements visible in the storyboard, with none of the details from the Vehicle you selected:

Before Data Hookup Detail

What's up with that?

Open VehicleDetailViewController.m; you'll see that while the UI was constructed in the Storyboard and all the IBOutlets were already hooked up to save you some time fighting with AutoLayout, none of the data is hooked up.

Note: You'll notice that several of the IBOutlets are set up in the VehicleDetailViewController.m instead of in the .h file, as they normally would be.

If you have properties you don’t want to be publicly available to other classes, you can always add them to your .m file in the private implementation. This is the @interface that’s declared at the top of a .m file and noted by the parentheses after the class name. For example, UIViewController() would be the private implementation of UIViewController.

Any @property declared in that interface can still be accessed as an IBOutlet (if properly annotated as such) in your Storyboard and within your .m implementation file, but it won't be accessible to any unrelated classes or to subclasses of your class.

Note: You'll notice that several of the IBOutlets are set up in the VehicleDetailViewController.m instead of in the .h file, as they normally would be.

If you have properties you don’t want to be publicly available to other classes, you can always add them to your .m file in the private implementation. This is the @interface that’s declared at the top of a .m file and noted by the parentheses after the class name. For example, UIViewController() would be the private implementation of UIViewController.

Any @property declared in that interface can still be accessed as an IBOutlet (if properly annotated as such) in your Storyboard and within your .m implementation file, but it won't be accessible to any unrelated classes or to subclasses of your class.

Contributors

Over 300 content creators. Join our team.