Sei sulla pagina 1di 14

Intro To Object-Oriented Design: Part

2/2
Ellen Shapiro on November 15, 2013

This post is also available in: Chinese (Simplified)


In Part 1 of this tutorial, you learned the basics of objectoriented design: objects, inheritance, and the model-viewcontroller pattern. You created the beginnings of a simple
application called Vehicles to help you gain a better
understanding of these concepts.
Here in the second part, youre going to learn about
Polymorphism and a couple of other key patterns in ObjectOriented programming: The Class Factory Method and the
Singleton Instance.
If you completed the previous tutorial, great! You can either pick
up where you left off with your previous project. However, if you
want to jump right into things, you can download the
completed project from Part 1 to get started.

Polymorphism
The general definition of Polymorphism comes from its Greek
roots Poly means many, and Morph means forms.

Wheels, engines, movementalike, yet


different.

The computer-science specific definition, pulled from the Free


Online Dictionary of Computing, is:
A variab le that may refer to ob jects whose class is not known at compile time and which respond at run time
according to the actual class of the ob ject to which they refer.
The combination of these definitions boils down to the ability of an object to be more than one thing at a time.
Sounds like your average iOS developer, doesnt it? ;]
There are several subtypes of polymorphism that are used within Objective-C, but two key ones youll see often are
the Decorator pattern and the Adapter pattern.

The Decorator Pattern


From Apples Cocoa Fundamentals guides section on Cocoa Design Patterns:
The Decorator design pattern attaches additional responsib ilities to an ob ject dynamically. Decorators provide a
flexib le alternative to sub classing for extending functionality. As does sub classing, adaptation of the Decorator
pattern allows you to incorporate new b ehavior without modifying existing code. Decorators wrap an ob ject of the
class whose b ehavior they extend.
The primary example of the Decorator pattern in Objective-C is the use of Categories.
Categories are a special kind of class in iOS that allow you to add additional methods to classes without having to

subclass or alter the original class. Theyre primarily used for adding methods to the stock UIKit components that
come with iOS.
The difference between a category and a subclass is pretty simple: A category allows you to add new methods, but
not override existing methods. You cant add new properties or instance variables to categories you can only use
existing ones. If you want to add a new property or instance variable, youll need to create a subclass and use the
power of inheritance to create your additional properties and methods.
But what if you dont need to do that? What if you just want to create a simple way to encapsulate something you
have to do repeatedly with a particular UIKit object? In this case, a category is the perfect solution.
In your tutorial app, youre going to add a convenience method to UIAlertView to do away with performing the
alloc-init-showdance for simple alerts over and over again in your code.

Implementing the Decorator Pattern


Go to File\New\File\Cocoa Touch, and select Objective-C Category:

Add the category name as Convenience as a category on UIAlertView:

Once youve created the files, you can see by their filenames the syntax Xcode uses to indicate that a file is a
category, as shown below:

The [Component]+[Category Name] format indicates both the original class being decorated and what the category
itself does. Its completely acceptable to use multiple categories on the same class in the same application; this
makes it easier to reuse categories in other applications.
Creating a method on a category is very similar to creating a method on a normal class. Since youre going to be
creating a new instance of UIAlertView rather than working with an existing instance, open up
UIAlertView+Convenience.h and add the following class declaration for your method after the @interfaceline:
// Shows a UIAlertView with the given title and message, and an OK button to dismiss
it.
+ (void)showSimpleAlertWithTitle:(NSString *)title andMessage:(NSString *)message;
Then, open UIAlertView+Convenience.m and add the method implementation:
+ (void)showSimpleAlertWithTitle:(NSString *)title andMessage:(NSString *)message
{
UIAlertView *simpleAlert = [[UIAlertView alloc] initWithTitle:title
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[simpleAlert show];
}
Youre not doing anything revolutionary here youre just combining a bunch of the boilerplate code youd to write
over and over and over to show a simple alert view with a single button to dismiss it.
Next, open VehicleDetailViewController.m and add the following import:
#import "UIAlertView+Convenience.h"
Towards the bottom of the file, youll find several IBActionmethods with just TODOcomments in the method body.
Update goForward, goBackward, stopMoving, and makeNoiseas shown below to use your new category:

-(IBAction)goForward
{
[UIAlertView showSimpleAlertWithTitle:@"Go Forward" andMessage:[self.detailVehicle
goForward]];
}
-(IBAction)goBackward
{
[UIAlertView showSimpleAlertWithTitle:@"Go Backward" andMessage:[self.detailVehicle
goBackward]];
}
-(IBAction)stopMoving
{
[UIAlertView showSimpleAlertWithTitle:@"Stop Moving" andMessage:[self.detailVehicle
stopMoving]];
}
-(IBAction)makeNoise
{
[UIAlertView showSimpleAlertWithTitle:@"Make Some Noise!" andMessage:
[self.detailVehicle makeNoise]];
}
Build and run your application; after selecting a vehicle, press any button except the Turn button, and youll see
the appropriate message for each instance of a Vehicle. For example, if you press the Make Some Noise! button
for various Vehicles, youll see the following:

But what if you need to do something a bit more complicated something that requires getting information from the
UIAlertView that youve shown? This is where the Adapter pattern and its use of delegation comes in handy.

The Adapter Pattern


Again from the Cocoa Fundamentals Guide:
The Adapter design pattern converts the interface of a class into another interface that clients expect. Adapter lets
classes work together that couldnt otherwise b ecause of incompatib le interfaces. It decouples the client from the
class of the targeted ob ject.
Protocols are the primary example of the Adapter pattern in Objective-C. This designates a number of methods that
can be implemented by any class. Theyre most often used for DataSourceand Delegatemethods, but can also
be used to help two unrelated classes communicate with each other.
The advantage of this pattern is that as long as a class declares that it conforms to the protocol, it really doesnt
matter whether its a model, a view, or a controller. It simply needs to know what is happening in the other class, and
will implement any required methods needed to know about this.

In order to help figure out how many degrees the user wants to turn a vehicle, youll take advantage of the
UIAlertViewDelegate protocol to get the information the user enters into a UIAlertView.

Implementing the Adapter Pattern


Open VehicleDetailViewController.h and declare that it conforms to the UIAlertViewDelegate protocol by adding
that protocols name in angle brackets, as so:
@interface VehicleDetailViewController : UIViewController <UIAlertViewDelegate>
If you were to translate the above line into English, it would read: This is a VehicleDetailViewController, which is a
subclass of UIViewController and conforms to the UIAlertViewDelegate protocol. If a class conforms to multiple
protocols, you can list them all in the angle brackets separated by commas.

Note: Implementing a specific protocol in a class is frequently called conforming to that protocol.

Youll use all this to implement a mechanism that figures out how many degrees the user wants to turn their
Vehicle.
Open VehicleDetailViewController.m and replace turnwith the following implementation:
-(IBAction)turn
{
//Create an alert view with a single text input to capture the number of degrees
//to turn your vehicle. Set this class as the delegate so one of the delegate
methods
//can retrieve what the user entered.
UIAlertView *turnEntryAlertView = [[UIAlertView alloc] initWithTitle:@"Turn"
message:@"Enter number of degrees to turn:" delegate:self cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Go!", nil];
turnEntryAlertView.alertViewStyle = UIAlertViewStylePlainTextInput;
[[turnEntryAlertView textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeNumberPad];
[turnEntryAlertView show];
}
The method creates a UIAlertView with a text input that will prompt the user for a numeric value.
Next, youll need to add a delegate method for the UIAlertView instance to call back after the user enters a number.
Add the following method:
#pragma mark - UIAlertViewDelegate method
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
//Note: Only one alert view will actually declare this class its delegate, so we
can
//
proceed without double-checking the alert view instance. If you have more
than
//
one alert view using the same class as its delegate, make sure you check
which
//
UIAlertView object is calling this delegate method.
if (buttonIndex != alertView.cancelButtonIndex) {
//Get the text the user input in the text field
NSString *degrees = [[alertView textFieldAtIndex:0] text];
//Convert it from a string to an integer
NSInteger degreesInt = [degrees integerValue];
//Use the simple alert view to display the information for turning.
[UIAlertView showSimpleAlertWithTitle:@"Turn" andMessage:[self.detailVehicle
turn:degreesInt]];
} //else the user has cancelled, and we don't need to do anything.

}
The above code implements a selected UIAlertViewDelegate method so you can detect when a button is clicked.
Build and run your project; select a Vehicle from the list, tap the Turn button and enter a number of degrees to turn,
like so:

If you hit Cancel, nothing will happen since youve set your delegate to ignore that index. However, if you hit Go!, the
first UIAlertView disappears and the following UIAlertView will appear:

Your application is now functionally complete. However, what if you want to make your code a little more elegant so
its easier to maintain and add to it later? Its time to learn about two more object-oriented design patterns to make
your coding life easier!

Additional Object-Oriented Patterns


While there are a ton of great patterns you can use with Object-Oriented programming (in fact, Eli Ganem has written
an entire tutorial full of them), there are two that are quite useful in your Vehicles apps context: the Class Factory

Method and the Singleton Instance.


Both of these patterns are used extensively in iOS development, and understanding what they do under the hood will
help you understand the design of the code youll encounter as an iOS developer.

Class Factory Methods


The concept of a Class Factory method is to return an instantiated object of a particular class with as much data prepopulated as possible. One of the obvious benefits of using a factory method rather than calling alloc/initand
setting properties, is that your code can be much shorter.
In this way, youre using one method to create an object and set all its properties rather than one method to create
the object, then writing multiple lines to set all the properties. However there are also two less-obvious benefits to
this technique.
One, it forces anyone using your class to provide exactly the information you need in order to create a fully
functioning instance of your object. As you created the various objects in this tutorial, you may have noticed that it
was easy to miss a property or two. With a factory method, you are forced to be conscious of exactly what information
you are and are not providing about the object you want to create.
Two, and admittedly much less of a problem with ARC code, which is pretty much anything written since iOS 5, is
that factory methods will return autoreleased objects, freeing the caller from having to release them later. You likely
wont need to worry about this issue unless youre supporting old code, but its a good thing to be aware of.

Implementing the Vehicle Class Factory Method


Open Vehicle.h and declare the following class factory method which accepts parameters representing all of the
basic properties of a vehicle:
//Factory Method
+ (instancetype)vehicleWithBrandName:(NSString *)brandName modelName:(NSString
*)modelName modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:
(NSInteger)numberOfWheels;
The instancetypetype is a slightly safer version of id. Whereas an idparameter or return type can accept any
subclass of NSObject, instancetypeas a method signature tells you that you are definitely receiving an instance
of the class or subclass of the class where the instance is being initialized.

Note: Read up on the details of instancetypeover on NSHipster

One other thing to note with factory methods and inheritance: since they return a fully instantiated object, you have to
be careful about how you use them in superclasses, as they return a particular class of object.
Go to Vehicle.m and add the following implementation of the factory method:
#pragma mark - Factory method
+ (instancetype)vehicleWithBrandName:(NSString *)brandName modelName:(NSString
*)modelName modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:
(NSInteger)numberOfWheels;
{
//Use self in the superclass to ensure you're getting the proper return type for
each of the subclasses.
Vehicle *newVehicle = [[self alloc] init];
//Set the provided values to the appropriate instance variables.
newVehicle.brandName = brandName;
newVehicle.modelName = modelName;
newVehicle.modelYear = modelYear;
newVehicle.powerSource = powerSource;
newVehicle.numberOfWheels = numberOfWheels;

//Return the newly created instance.


return newVehicle;
}
The factory method here initializes the object and sets up the properties. Since Vehicle has subclasses, you need to
make sure youre using [[self alloc] init]rather than [[Vehicle alloc] init]. That way, subclasses like
Car can also use this inherited factory method to get Car objects back rather than Vehicle objects.

Note: Quality Coding has a great article How to Botch Your Objective-C Factory Method, that goes into a lot
more depth on this subject.

Implementing the Car Class Factory Method


Go to Car.h and declare the following factory method:
//Factory Method
+(Car *)carWithBrandName:(NSString *)brandName modelName:(NSString *)modelName
modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource numberOfDoors:
(NSInteger)numberOfDoors convertible:(BOOL)isConvertible hatchback:(BOOL)isHatchback
sunroof:(BOOL)hasSunroof;
Since you need all the information for the Vehicle other than the number of wheels, youve added parameters to your
method for all the other Vehicle data along with the Car-specific properties.
Go to Car.m and replace initwith the following implementation of the factory method:
#pragma mark - Factory Method
+(Car *)carWithBrandName:(NSString *)brandName modelName:(NSString *)modelName
modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource numberOfDoors:
(NSInteger)numberOfDoors convertible:(BOOL)isConvertible hatchback:(BOOL)isHatchback
sunroof:(BOOL)hasSunroof
{
//Create the car object using the superclass factory method.
Car *newCar = [Car vehicleWithBrandName:brandName modelName:modelName
modelYear:modelYear powerSource:powerSource wheels:4];
//Set the car-specific properties using the passed-in variables.
newCar.numberOfDoors = numberOfDoors;
newCar.isConvertible = isConvertible;
newCar.isHatchback = isHatchback;
newCar.hasSunroof = hasSunroof;
//Return the fully instantiated Car object.
return newCar;
}
Note that as a general rule of thumb, you dont have to choose between an initmethod and a factory method;
however, in this case youre not going to be using the initmethod directly and the factory method now does
everything your custom initmethod used to do. It makes sense at this point to get rid of the old code since you
wont be needing it anymore.
Next, go into VehicleListTableViewController.m and update the setupVehicleArraymethod to use the new
factory method on each of the Car objects youre creating, as follows:
//Create a car.
Car *mustang = [Car carWithBrandName:@"Ford" modelName:@"Mustang" modelYear:1968
powerSource:@"gas engine" numberOfDoors:2 convertible:YES hatchback:NO
sunroof:NO];
//Add it to the array

[self.vehicles addObject:mustang];
//Create another car.
Car *outback = [Car carWithBrandName:@"Subaru" modelName:@"Outback" modelYear:1999
powerSource:@"gas engine" numberOfDoors:5 convertible:NO hatchback:YES
sunroof:NO];
//Add it to the array.
[self.vehicles addObject:outback];
//Create another car
Car *prius = [Car carWithBrandName:@"Toyota" modelName:@"Prius" modelYear:2007
powerSource:@"hybrid engine" numberOfDoors:5 convertible:YES hatchback:YES
sunroof:YES];
//Add it to the array.
[self.vehicles addObject:prius];
Build and run your application; everything looks the same as it did before, but you know that underneath the hood
youre using far less code to creating your Vehicle array. You can now take this same pattern and apply it to the
Motorcycle and Truck classes.

Implementing the Motorcycle Class Factory Method


In Motorcycle.h, add the following new declaration of the factory method
//Factory Method
+(Motorcycle *)motorcycleWithBrandName:(NSString *)brandName modelName:(NSString
*)modelName modelYear:(NSInteger)modelYear engineNoise:(NSString *)engineNoise;
In this case, youre adding the specific parameters for creating a new instance of Motorcycles.
Now open up Motorcycle.m and replace the initmethod with your factory methods implementation, as below:
#pragma mark - Factory Method
+(Motorcycle *)motorcycleWithBrandName:(NSString *)brandName modelName:(NSString
*)modelName modelYear:(NSInteger)modelYear engineNoise:(NSString *)engineNoise
{
//Create a new instance of the motorcycle with the basic properties by calling the
Factory
//method on the superclass.
Motorcycle *newMotorcycle = [Motorcycle vehicleWithBrandName:brandName
modelName:modelName modelYear:modelYear powerSource:@"gas engine" wheels:2];
//Set the Motorcycle-specific properties.
newMotorcycle.engineNoise = engineNoise;
return newMotorcycle;
}

Implementing the Truck Class Factory Method


Open Truck.h and add the following factory method declaration:
//Factory Method
+(Truck *)truckWithBrandName:(NSString *)brandName modelName:(NSString *)modelName
modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:
(NSInteger)numberOfWheels cargoCapacityCubicFeet:(NSInteger)cargoCapacityCubicFeet;
Just as before, youre including parameters that are specific to your new Vehicle instance in this case, Truck.
Open Truck.m, and add the following factory method implementation (in this case, there isnt an existing initto
replace):

#pragma mark - Factory Method


+(Truck *)truckWithBrandName:(NSString *)brandName modelName:(NSString *)modelName
modelYear:(NSInteger)modelYear powerSource:(NSString *)powerSource wheels:
(NSInteger)numberOfWheels cargoCapacityCubicFeet:(NSInteger)cargoCapacityCubicFeet
{
//Create a new instance using the superclass's factory method.
Truck *newTruck = [Truck vehicleWithBrandName:brandName modelName:modelName
modelYear:modelYear powerSource:powerSource wheels:numberOfWheels];
newTruck.cargoCapacityCubicFeet = cargoCapacityCubicFeet;
//Return the newly created truck instance.
return newTruck;
}
Now that youve created your factory methods for Motorcycle and Truck, head back to
VehicleDetailsViewController.m and update your code to use these new factory methods as shown below:
//Add a motorcycle
Motorcycle *harley = [Motorcycle motorcycleWithBrandName:@"Harley-Davidson"
modelName:@"Softail" modelYear:1979 engineNoise:@"Vrrrrrrrroooooooooom!"];
//Add it to the array.
[self.vehicles addObject:harley];
//Add another motorcycle
Motorcycle *kawasaki = [Motorcycle motorcycleWithBrandName:@"Kawasaki"
modelName:@"Ninja" modelYear:2005 engineNoise:@"Neeeeeeeeeeeeeeeeow!"];
//Add it to the array
[self.vehicles addObject:kawasaki];
//Create a truck
Truck *silverado = [Truck truckWithBrandName:@"Chevrolet" modelName:@"Silverado"
modelYear:2011 powerSource:@"gas engine" wheels:4 cargoCapacityCubicFeet:53];
[self.vehicles addObject:silverado];
//Create another truck
Truck *eighteenWheeler = [Truck truckWithBrandName:@"Peterbilt" modelName:@"579"
modelYear:2013 powerSource:@"diesel engine" wheels:18
cargoCapacityCubicFeet:408];
[self.vehicles addObject:eighteenWheeler];
Build and run your application; again, everything works exactly the same on the surface. However, youve shortened
and simplified your code under the hood and moved often-repeated code into reusable factory methods.
Factory methods add a level of convenience and guard against the possibility of inadvertently leaving off a required
property. Youll see them in common places such as NSStrings stringWithFormat:or UIButtons
buttonWithType: and now youve added them to your own vehicle class and subclasses!

The Singleton Pattern


One very specific, very useful type of Class Factory method is the Singleton. This ensures that a particular instance
of a class is only initialized once.
This is great for items that need to only have a single instance for instance, the UIApplication singleton
sharedApplication or for those classes that are expensive to initialize, or which store small amounts of data
which need to be accessed and updated throughout your app.
In the case of your Vehicles app, you can see theres one piece of data that might need to be accessed and updated
all over the place: your list of Vehicles. The list also violates MVC rules by letting VehicleListTableViewController
manage its creation and existence. By moving the list of vehicles into its own singleton class, you gain a lot of

flexibility for the future.


Go to File\New\File\Objective-C Class and create a subclass of NSObject called VehicleList. Open VehicleList.h
and add the following class method declaration for the singleton instance along with a property to store the array of
vehicles under the @interfaceline:
//The list of vehicles.
@property (nonatomic, strong) NSArray *vehicles;
//Singleton Instance
+ (VehicleList *)sharedInstance;
Next, open VehicleList.m and add the following implementation of the singleton factory method:
+ (VehicleList *)sharedInstance
{
//Declare a static instance variable
static VehicleList *_vehicleList = nil;
//Create a token that facilitates only creating this item once.
static dispatch_once_t onceToken;
//Use Grand Central Dispatch to create a single instance and do any initial setup
only once.
dispatch_once(&onceToken, ^{
//These are only invoked the onceToken has never been used before.
_vehicleList = [[VehicleList alloc] init];
_vehicleList.vehicles = [VehicleList initialVehicleList];
});
//Returns the shared instance variable.
return _vehicleList;
}
Notice that youre declaring the _vehicleListinstance variable and onceTokenGCD token as staticvariables.
This means that the variable exists for the entire lifetime of the program. This helps with creating a singleton in two
ways:
1. Instead of checking the null/not null status of the _vehicleListinstance variable, GCD can more quickly test
whether the onceTokenhas been executed or not in order to decide whether it needs to create the
_vehicleListinstance. Using GCD to perform this check is also thread-safe, since dispatch_once
ensures that when its called from multiple threads, each thread will finish before the next one is allowed to try
and create the instance.

2. You cant accidentally overwrite the _vehicleListinstance. Since static variables can only be initialized once,
if someone accidentally adds another [[VehicleList alloc] init]call once the _vehicleListvariable
has an initialized object, it wont have any effect on your existing VehicleList object.
Next, you need to move your vehicle creation over from the VehicleListTableViewController to the VehicleList class.
First, import the Car, Motorcycle, and Truck classes at the top of VehicleList.m:
#import "Car.h"
#import "Motorcycle.h"
#import "Truck.h"
Next, add the following class method to VehicleList.m:
+ (NSArray *)initialVehicleList
{
//Initialize mutable array.
NSMutableArray *vehicles = [NSMutableArray array];

//Create a car.
Car *mustang = [Car carWithBrandName:@"Ford" modelName:@"Mustang" modelYear:1968
powerSource:@"gas engine" numberOfDoors:2 convertible:YES hatchback:NO
sunroof:NO];
//Add it to the array
[vehicles addObject:mustang];
//Create another car.
Car *outback = [Car carWithBrandName:@"Subaru" modelName:@"Outback" modelYear:1999
powerSource:@"gas engine" numberOfDoors:5 convertible:NO hatchback:YES
sunroof:NO];
//Add it to the array.
[vehicles addObject:outback];
//Create another car
Car *prius = [Car carWithBrandName:@"Toyota" modelName:@"Prius" modelYear:2007
powerSource:@"hybrid engine" numberOfDoors:5 convertible:YES hatchback:YES
sunroof:YES];
//Add it to the array.
[vehicles addObject:prius];
//Add a motorcycle
Motorcycle *harley = [Motorcycle motorcycleWithBrandName:@"Harley-Davidson"
modelName:@"Softail"
modelYear:1979 engineNoise:@"Vrrrrrrrroooooooooom!"];
//Add it to the array.
[vehicles addObject:harley];
//Add another motorcycle
Motorcycle *kawasaki = [Motorcycle motorcycleWithBrandName:@"Kawasaki"
modelName:@"Ninja"
modelYear:2005 engineNoise:@"Neeeeeeeeeeeeeeeeow!"];
//Add it to the array
[vehicles addObject:kawasaki];
//Create a truck
Truck *silverado = [Truck truckWithBrandName:@"Chevrolet" modelName:@"Silverado"
modelYear:2011
powerSource:@"gas engine" wheels:4 cargoCapacityCubicFeet:53];
[vehicles addObject:silverado];
//Create another truck
Truck *eighteenWheeler = [Truck truckWithBrandName:@"Peterbilt" modelName:@"579"
modelYear:2013
powerSource:@"diesel engine" wheels:18 cargoCapacityCubicFeet:408];
[vehicles addObject:eighteenWheeler];
//Sort the array by the model year
NSSortDescriptor *modelYear = [NSSortDescriptor sortDescriptorWithKey:@"modelYear"
ascending:YES];
[vehicles sortUsingDescriptors:@[modelYear]];
return vehicles;
}
The above method can be called at any time to either create or reset the initial list of vehicles.
Youll notice that most of this code has been moved over from VehicleListTableViewController, but now the

vehicles are added to the newly created local vehiclesarray instead of VehicleListTableViewControllers
self.vehicles.
Now you can go back to VehicleListTableViewController.m and remove three things that are no longer needed:
1. Delete the entire setupVehiclesArraymethod and the call to it in awakeFromNib.
2. Delete the vehiclesinstance variable and the call to initialize it in awakeFromNib.
3. Delete the #importsfor Car.h, Motorcycle.h, and Truck.h
Your private interface for VehicleListTableViewController and awakeFromNibimplementation should now look like
this:
@interface VehicleListTableViewController ()
@end
@implementation VehicleListTableViewController
#pragma mark - View Lifecycle
- (void)awakeFromNib
{
[super awakeFromNib];
//Set the title of the View Controller, which will display in the Navigation bar.
self.title = @"Vehicles";
}
Youll notice that Xcode shows you have three errors, since there are three places where you used the vehicles
property to feed the UITableViewDataSource and segue handling methods. Youll need to update these to use your
new singleton instead.
First, import the VehicleList class at the top of VehicleListTableViewController.m so you can access the singleton:
#import "VehicleList.h"
Then, find the three spots where Xcode indicates an error and update the code to use the VehicleList singletons
array of vehiclesinstead, as shown below:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
return [[VehicleList sharedInstance] vehicles].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"
forIndexPath:indexPath];
Vehicle *vehicle = [[VehicleList sharedInstance] vehicles][indexPath.row];
cell.textLabel.text = [vehicle vehicleTitleString];
return cell;
}
#pragma mark - Segue handling
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Vehicle *selectedVehicle = [[VehicleList sharedInstance] vehicles]
[indexPath.row];
[[segue destinationViewController] setDetailVehicle:selectedVehicle];
}

}
Build and run your application; youll see the same list as you did before, but you can sleep better knowing that the
code behind the app is clean, concise, and easily extensible.
With all the changes above, youll be able to easily add new Vehicles to this list in the future. For instance, if you
were to add a new UIViewController that allows the user to add their own Vehicle, youd only need to add it to the
singletons Vehicles array.
Or, if you wanted to allow the user to edit a Vehicle, you could make sure you sent all the data back without needing
to implement a delegatefor the VehicleListViewController.
Theres one tricky thing to watch out for with singletons: they will stay alive for the entire duration of your apps
lifecycle, therefore you dont want to load them down with too much data. They can be great for lightweight data
storage or to make objects accessible throughout your application.
If youre storing a lot of data in your app, youll want to look at something more robust like Core Data to handle the
data storage and retrieval of your objects.

Where To Go From Here?


In one single app, youve created a clean, object-oriented application using basic objects, inheritance, MVC design,
polymorphism, as well as singleton and factory methods. You can review the source code of the finished project
here.
For more information on singletons, you can read Mike Ashs definitive, incredibly detailed post on the Care and
Feeding of Singletons.
If youd like more information about Object-Oriented patterns, Eli Ganem wrote a great tutorial called iOS Design
Patterns that reviews a bit of what youve learned here, then introduces you to several more advanced design
patterns that you can use to construct even more elegant code.
If youve got questions, please ask away in the comments below!

designatednerd
Ellen Shapiro is a mob ile developer in Chicago, Illinois who b uilds iOS and Android
apps for Vokal Interactive, and is working in her spare time to help b ring Hum to life.
Shes also developed several independent applications through her personal
company, Designated Nerd Software.
When she's not writing code, she's usually tweeting ab out it.

Potrebbero piacerti anche