Hour 2. Object-Oriented Programming with Objective-C


What You’ll Learn in This Hour

Image Understanding Objective-C and its objectives

Image Using object-oriented principles and good programming style

Image How inheritance works in the Objective-C world


Object-Oriented Programming in the Objective-C World

Object-oriented programming is probably the most commonly used programming paradigm today, but there is no clear definition of what it is. The most common informal description (not definition) is that it is a style of programming in which the basic building blocks are objects—combinations of methods and variables that represent concepts or physical objects in a program. The ability to build a program in which the program components map directly to objects and concepts in the physical world can make it easier for people to develop software because on one level they are dealing with the objects and concepts about which they are trying to build software.

Beyond this basic concept of objects, a variety of other concepts can come into play. These concepts include data abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance. (That list is from the Wikipedia article on object-oriented programming.) Objective-C incorporates all of those, and in this hour you find out more about data abstraction, encapsulation, and modularity. The other concepts are introduced in subsequent hours.

Implementing Object-Oriented Programming

Although this book focuses on Objective-C and does not compare it with other languages, it is important to point out that in the 1960s when object-oriented programming became popular, there was disagreement over how to go about developing actual programs. There were two schools of thought:

Image Object-oriented languages—Over the years, a number of languages have been built specifically to work with object-oriented programming. These included Simula, Ruby, Eiffel, and Smalltalk.

Image Object-oriented additions—Partly because of the difficulty of retraining programmers on a totally new language, many hybrid languages have been developed. These include C++ and C# as well Objective-C. Object-oriented features have been added to languages such as PHP and even FORTRAN and COBOL.

Building Object-Oriented Projects

When you have an object-oriented language, you can start to build object-oriented projects, but that is just the starting point. There are several ways to proceed, but because Objective-C is used almost exclusively for building OS X and iOS projects, the focus in this section is on them.

One of the major objectives of object-oriented programming is making it easier to reuse existing code. When you encapsulate code into formal objects with well-structured interfaces, you should be able to reuse that code, and your investment in it can pay off in a variety of later uses.

Code reuse is a critical component of Apple’s development environments. Object-oriented programming and its implementation in Objective-C provide the basis of reuse. The structure of reuse on iOS and OS X relies on frameworks. Frameworks are sets of related classes (and a few nonclass entities) that implement functional areas of interest to developers. Frameworks are essentially organizational tools for the classes they contain. Your involvement with frameworks consists of importing the ones necessary to your project; you actually interact with, use, and subclass the classes within a framework. (It is also possible to create your own frameworks, but that is beyond the scope of this book. You can find documentation of the process on developer.apple.com.)

The set of frameworks that you deal with forms either Cocoa or Cocoa Touch. Each has two aspects: one for runtime and one for development.


Tip

In this book, if no distinction is made regarding the environment, then you can assume the reference is to both environments.


When you create Cocoa projects, you commonly start from a template in Xcode. (You also may start from an example on developer.apple.com.) In both cases, the import directives for the appropriate frameworks are already provided, but you can also add any necessary import directives.

Looking at the Frameworks

You will most commonly deal with two sets of frameworks. As noted previously, these are primarily organizational structures; you find references to them in the documentation you see in Xcode’s Organizer and elsewhere. On OS X, the various frameworks themselves are organized into five layers:

Image User Experience

Image Application Frameworks

Image Graphics and Media (Application Services)

Image Core Services

Image Underpinning the whole structure is the Darwin kernel

On iOS, there are five layers:

Image Application

Image Cocoa Touch

Image Media

Image Core Services

Image Core OS

As is common in layered architectures, each layer can refer to layers below it but cannot refer to layers above it. Thus, frameworks in Core Services can rely on classes in Core OS (iOS) and Darwin (OS X), but not on anything in the higher layers, such as media and interface-handling classes.

Within this structure, there are two frameworks on which your projects almost always rely. On OS X, they are

Image AppKit in Application Frameworks

Image Core Foundation in Core Services

On iOS, they are

Image UIKit in Cocoa Touch

Image Foundation in Core Services

Although the frameworks are collections of classes for the most part, they also contain some other entities. In particular, Core Foundation and Foundation contain C elements such as structs and typedefs. This ability to use native C in an Objective-C project is important to this structure.

Creating C with Objects

Objective-C started from C, “the language of the UNIX operating system,” as Brian W. Kernighan and Dennis M. Ritchie put it in the introduction to their classic book, The C Programming Language. C itself is a relatively terse language (the preface to the first edition refers to its “economy of expression”). C played a critical role in the development of object-oriented languages around 1980: Both C++ and Objective-C started as C programs to convert their native syntax to compilable C code. C++ was heavily influenced by Simula, whereas Objective-C’s main influence was Smalltalk. The relative merits of the two object-oriented offspring of C have been hotly debated over the years.

Today, Objective-C is perhaps most widely known as the language of OS X and iOS. Comparisons to other languages are largely irrelevant. What does matter is the basic C syntax that is outlined for you in Appendix A, “C Syntax Summary,” if you need a refresher.

Start Thinking in Objective-C

Just as with a natural language, if you start thinking in a language you know well and then mentally translate it into the new language, it takes you much longer to learn the new language. Much of what you know about computer languages in general still applies in Objective-C, and the basics of object-oriented programming apply as well. For many people, the biggest difference between Objective-C and languages such as C itself or C++ is that Objective-C looks different. The next section introduces you to the heart of this difference: methods and messaging.


Tip

Don’t translate the concepts and syntax of the following sections. Use what natural language instructors call total immersion: Jump right in and start writing Objective-C.


Understanding Data Abstraction

Data abstraction is one of the key principles behind Objective-C. Abstraction is not simply a computer term; it is widely used in other areas. It is the process of considering a number of ideas or objects together and leaving out the differences among them so that what remains is the abstraction that is common to all of them. It can be an exercise in logic or philosophy or a children’s game such as, “What do these things have in common?” If the things are a monkey, a goldfish, and a person, the answer might be that they are all animals. That is one abstraction of those objects.

A key part of abstraction is hiding details that are irrelevant to the abstraction so that the abstracted information is more recognizable. That aspect of data abstraction is critical in Objective-C. In the language, its use, and its best practices, the hiding of object details makes the abstractions usable. Hiding is very important in Objective-C.

When you have abstract objects with their differences hidden, you can write code to manipulate the objects. With the differentiations hidden, your code can safely ignore the differences. It is important to learn and remember this principle of hiding differentiations from the abstract objects. Many people mistakenly start out with Objective-C (and other languages, but the focus here is on Objective-C) by dealing with the differentiations that should be hidden.

Thus, if you create a bank account class, to the outside world it should look and function the same way whether it is a savings account or a checking account. The class probably can report its current balance and perhaps pending transactions. Subclasses might be able to report attributes specific to savings or checking accounts, but the abstract bank account class probably cannot do that because reporting on checks that have not yet cleared is not something that a savings account can do (unless it is a hybrid account where you can write checks against the savings account).

Considering Classes and Instances

As is the case in many programming languages, you can define entities in your code. You can write functions and methods in many languages, and you can declare classes in many object-oriented languages. However, having written a function or method means very little unless you actually call it. By the same token, declaring a class has no meaning unless you actually create an instance of the class.

Exploring Encapsulation

Encapsulation is related to data abstraction, and it can be demonstrated by writing some pseudocode for a bank account class, as shown in Listing 2.1.

Although this is pseudocode, it begins to introduce a few Objective-C syntax elements that are described in detail in Part II, “Working with the Objective-C Basics.”

LISTING 2.1 Pseudocode for a Bank Account Class


#import "BankAccount.h"

@implementation BankAccount

#define checking 1
#define saving 2
#define investment 3

– (float)balance: (int) accountType
{
  switch (accountType) {
    case checking:
      //set returnValue ...
      break;

    case saving:
      //set returnValue ...
      break;

    case investment:
      //set returnValue ...
      break;

    default:
      //set returnValue ... (maybe to nil)
      break;
  }
  return returnValue;

}

@end


This is a method of a bank account class that returns the balance for that account. You pass in the account type, and depending on the account type, different routines fire to return the necessary value.

The structure shown here demonstrates the principle of encapsulation by breaking it. The bank account class does abstract the particularities of each type of account. You call this method and pass in whatever type of account you are interested in, so you might think that you have abstracted the class data.

But the notion of encapsulation added to data abstraction suggests that the abstract object should be self-sufficient. If you have an instance of a bank account class, you should be able to ask it for its balance, and, without you passing in any additional information, the self-contained (encapsulated) class can do whatever is necessary.

This approach means that within the bank account class, you need to store the account type.

Listing 2.2 shows pseudocode for a bank account class header.

LISTING 2.2 Bank Account Header


@interface BankAccount : NSObject
{
  @private int accountType; //variable declaration
}

– (float *)balance; //method declaration

@end


Because the account type is stored within the object, the method to return the bank account’s balance already has that information. The code shown previously in Listing 2.1 can be converted to use that internal value, as shown in Listing 2.3.

LISTING 2.3 Bank Account Balance from an Internal Account Type Variable


#import "BankAccount.h"

@implementation BankAccount

#define checking 1
#define saving 2
#define investment 3

– (float *)balance
{
  switch (accountType) {
    case checking:
      //set returnValue ...
      break;

    case saving:
      //set returnValue ...
      break;

    case investment:
      //set returnValue ...
      break;

    default:
      //set returnValue ...
      break;
  }
  return returnValue;

}

@end


In Listing 2.2, the header of this class, you see that the account type variable is declared as private, which means that it is accessible only from within the class. The only part of the class that is visible to the outside world is the method that returns the balance. It does that based on its own internal data (the account type, and, presumably, transaction data). This bank account class is both abstracted and encapsulated. From the outside, all it can do is return its balance based on its internal values. Anyone accessing this class doesn’t need to know how it does what it does.

Note that in Listing 2.3 everything about the internal workings of the class is hidden. The values of the different types of bank accounts are not exposed. This type of structure might be useful to protect your code from external changes. When you create a bank account instance, you might set its account type by passing in a parameter that might be a string such as “Super Saver Account.” The translation of that string, which is controlled by the marketing area of the bank, to an internal type of account can happen when the specific bank account instance is created; from then on, the terminology of the marketing area is independent of the internal functions of the bank accounts. Likewise, you can change the functionality of an account type without changing anything that is exposed to the outside world. (This happens in banking when regulations change across a banking system, forcing new types of calculations.)

GO TO Image Hour 8, “Declaring Instance Variables in an Interface File,” p. 111, for more about setting the scope of instance variables, such as making them private, public, or protected.

Using Accessors to Manage Encapsulation

An important tool for managing encapsulation is the use of accessors, which are small methods that let you access the instance variables of an object without referring to them directly. Accessors sometimes simply retrieve or set the value of a variable, but they also may perform transformations and modifications. For example, in a number of framework accessor methods in the Core Data framework, an accessor creates an essential element of the Core Data stack if it does not yet exist. Having created it, the object can then return it quickly the next time it is asked for.

Managing Inheritance in the Objective-C World

As you see throughout this book, Objective-C approaches subclassing and inheritance in more varied ways than many other object-oriented languages do. The basics of inheritance are simple. You declare a base object, which may have its own methods and instance variables. Following good Objective-C programming practice, the instance variables are accessed through accessors and are not visible to other classes.

GO TO Image Hour 9, “Declaring Properties in an Interface File,” p. 127, to see how to use declared properties to automatically generate accessors.

You can declare class methods in the class interface (a .h file) or in the implementation file (a .m file). Methods declared in the interface file are visible to other classes, but you can hide methods from the outside world by declaring them inside the implementation. Hiding methods in the implementation file helps to increase the encapsulation of the class.

GO TO ImageUsing Class Extensions” in Hour 18, “Extending a Class with Categories and Extensions,” p. 245, to see how to hide your class’s methods.

You then proceed to override your base object. Each class that overrides the base object inherits the methods and variables of the base object. It can override them so that if your base object has a method called init, you can control how subclasses deal with that method. You have three basic choices:

Image Invoke—If your base class declares init, when you call init on a subclass, the base class’s init method is invoked.

Image Override—If you have declared an init method in a subclass, calling init on the subclass invokes the subclass’s init method.

Image Override and invoke—As part of a subclass’s override of a base class method, you can always call [super init] (or whatever the name of the base class method is).

This means that you can combine the functionality of the base class with additional or replacement functionality in its subclasses. If you subclass a subclass, you can use calls to super to invoke the various implementations of the method up through the base class. From the object-oriented standpoint, what you see is that the base object and its descendants all implement in one way or another this particular method—something that the abstract object should implement (such as reporting the balance in a bank account).

All of this is standard for object-oriented programming. Objective-C expands on the concept of inheritance in a variety of ways. Inheritance and subclassing provide an orderly way of integrating code from other objects into your own object. Primarily this is from superclasses of your own object.

However, Objective-C offers several ways to integrate code from other structures into your own object. In addition to inheritance, these are

Image Protocols—These are sets of methods that can be adopted by classes. The class that adopts the protocol then implements the methods of the protocol; if a class adopts a protocol, know that you can invoke any of the protocol’s methods. (A protocol’s methods can be marked as optional, and in that case you have to make certain that you are only invoking those methods that are required or are implemented.) Each class that adopts a protocol implements it in its own way, often with its own variables. The protocol structure in some ways implements some features of multiple inheritance. The methods of the protocol may appear in several classes that are otherwise unrelated. The idea that only the inheritance chain lets you share methods is augmented in this way.

Image Categories—Whereas protocols can be adopted by any class, categories consist of methods that are added to a specific class at runtime. You can use categories to add methods to an existing class for which you do not have the source code.

Image Extensions—Sometimes called anonymous categories, extensions are declared and implemented in the implementation of a class (typically the .m file). Extensions can add methods and properties to a class. They also can redefine properties (such as changing readonly to readwrite). They are now commonly used to declare methods and properties that are totally private to the class itself.

Summary

In this hour, you read about the design principle behind Objective-C as well as how it implements object-oriented programming objectives. In particular, you have seen how data abstraction and encapsulation are implemented in Objective-C so that you can write elegant, efficient, and maintainable object-oriented code. You have also seen how Objective-C implements a variety of architectures that allow for data reuse and sharing. With Objective-C, it is not just a matter of inheritance because you also have categories, extensions, and protocols to use.

Q&A

Q. Why do categories, extensions, and protocols matter in Objective-C?

A. They allow you to reuse code in other ways than by creating subclasses. This can mean that your class hierarchies in Objective-C might be flatter than they are in languages in which the only way to share code is to subclass it.

Q. Why does encapsulation matter?

A. Along with data abstraction, it means that your objects are self-contained. The only attributes and functions that are exposed to the outside world are those that are common to all instances of the class. This can make ongoing maintenance easier.

Workshop

Quiz

1. If you override a method of a superclass, is it automatically invoked as well?

2. Why are accessors important?

Quiz Answers

1. No. If you want to invoke the method of a superclass, you must call it with code such as [super myMethod]. Typically, that is either the first or last line of your override.

2. They allow you to set and get values for instance variables without directly touching the variables themselves. They also let you do some additional work such as adjusting or creating other variables and properties.

Activities

Explore the Cocoa frameworks on developer.apple.com or with Organizer in Xcode. If you read the overview of each one, you find a list of its classes, and you see which ones will be most important to you. Pay particular attention to UIKit in Cocoa Touch and AppKit in OS X.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.137.67