Hour 17. Extending a Class with Protocols and Delegates


What You’ll Learn in This Hour

Image Comparing subclassing with Objective-C techniques

Image Using a protocol

Image Understanding delegates

Image Exploring documentation and header files


Exploring the Pros and Cons of Subclassing

In general, this book focuses on Objective-C itself and leaves detailed comparisons with other object-oriented languages to other sources. Even though it has been a quarter-century since the rapid rise of object-oriented programming languages, some of the battles among partisans of those languages remain vivid and even bitter.

One of the purposes of object-oriented languages is to enable people to work with objects that can be similar to one another in a way that lets developers write only the code involved in the difference. If you have a bank account class, it could display the account owner’s name and address as well as other information that is relevant to all types of bank accounts.

If you subclass the bank account class, you can create a savings account. The code to display the owner’s name and address can be inherited from the basic bank account class, but for the calculation of daily interest, the savings account class might have to have its own code just as a checking account class might also have its own code. This object-oriented structure holds great promise for being able to make modifications that are confined in their effect (changing or creating a savings account class should not affect a checking account class). Code that has been written and tested in the context of a superclass can be invoked by subclasses with a fair degree of confidence the superclass will continue to function properly. The hope is that only the new code needs detailed testing.

Partisans of Objective-C point out that subclassing is in many ways a blunt instrument, and it is not the precise answer in many cases. Because your primary tool for differentiating related objects from one another is subclassing, you wind up constructing hierarchies that frequently are many layers deep. With Objective-C, a variety of techniques have been implemented that enable you to write code for a variation on an existing object without subclassing it. These are techniques in which structured sections of code can be written and managed without placing them into a class.

Introducing the Example

As an example, the Master-Detail Application template can be modified to use a protocol and delegate. Consider the possibility of editing data in a detail view controller. If you were to do so, you would need to save it when the user navigates away or taps a Save button. In the Master-Detail Application template, it is the master view controller that reads and writes data. It presents the list of detail items to the user, so it is the master view controller that sets up the appropriate detail view controller. Because it sets up the detail view controller, it knows when the detail item changes and can save any changes that have been made to it.

If there is a Save button, it would be in the Detail View Controller. So it seems that both master and detail view controllers need access to the code to save data.

The master view controller does have a reference to the detail view controller, but there isn’t a reverse reference. It is possible to locate the master view controller from the detail view controller, but many people would argue that this kind of circular referencing is poor style. A protocol and delegate can work together to solve the problem.


Tip

Although this is a simple issue that could be solved in other ways, it’s a good example of how a protocol can work without tangling up references and dependencies among classes. Another way to accomplish this goal is to use a notification. In this case, the detail view controller would not call the method on its delegate; instead, the detail view controller would notify any object that is listening for the notification that it should save the data.


Working with Protocols

Protocols can be described as a set of methods that are not part of a class. They can also be considered methods of an anonymous class because you know nothing about the class to which they may actually belong.

How can you have methods that are not part of a class? They would be methods that respond to messages (or perform operations) that are defined conceptually but not in specific terms for a class.

These methods are defined as part of a protocol rather than as part of a class. Classes can then adopt the protocol; they are expected to implement the methods declared in the protocol (subject to the fact that some of them are optional). Classes can adopt many protocols, but in practice the number is not much more than five with the exception of some of the framework classes.

Unlike classes, protocols do not let you declare instance variables, so it is only the methods and properties that are part of the protocol.

Working with Delegates

A delegate is an object to which a class instance hands off certain messages. The delegate is expected to handle those messages for the instance and take appropriate actions. The messages that the delegate handles are declared in a protocol. Both the protocol and the delegate need access to the protocol declaration, but only the implementer of the protocol needs access to the protocol definition and code. Thus, two objects that are linked by a protocol/delegate connection have access to the same functionality, but only one (the implementer) has access to the detailed definition.


Note

This is where the optional and required methods can become very useful. Thinking through what is optional and what is required can help you to focus a protocol. Some people believe that protocols with a fairly limited focus and with methods that are predominantly (or all) optional or required improves reusability.


Putting Protocols and Delegates Together

This section explains how you can use a protocol and delegate for the scenario described in the example. Putting it all together, here’s what you get:

1. The class that will implement the protocol declares it in most cases. You can declare it in other places and include it, but most commonly it is declared in the implementer. Figure 17.1 shows MasterViewController.h, which declares the protocol and then indicates that it will implement it the protocol with <DetailDelegateProtocol>.

Image

FIGURE 17.1 Declare the protocol and adopt it.

2. The class that adopts the protocol (which means that it promises to implement it) must do so. Figure 17.2 shows the implementation in MasterViewController.m.

Image

FIGURE 17.2 Implement the protocol.

3. The class that will use the protocol declares a delegate. As you can see in Figure 17.3, this delegate is often named delegate (but it doesn’t have to be), it is commonly typed as id (but, again, doesn’t have to be), and its declaration indicates that it must implement the required methods of the protocol (this does have to be). Note the forward @protocol statement. This keeps the code clean by not needing to import MasterViewController.h here in DetailViewController.h.

Image

FIGURE 17.3 Declare a delegate that will use the protocol.

4. A class that knows about the protocol and the instance that will use it must set the delegate of that class. In the example, MasterViewController implements the protocol. It also has a reference to the detail view controller. It therefore can set the delegate with this line of code:

Click here to view code image

self.detailViewController.delegate = self;

5. In DetailViewController.m, import MasterViewController.h and then use the protocol methods as shown in the highlighted code in Figure 17.4.

Image

FIGURE 17.4 The delegate can now use the protocol’s methods.

Looking Deeper Inside Protocols

As mentioned previously, a protocol’s methods can be optional. This is a relatively recent change to Objective-C. As a result, you will see code that might appear unclear. What is absolutely certain is that any methods declared in a protocol preceded by @optional are, indeed, optional. You cannot rely on their being implemented by classes that adopt the protocol.

Classes that are preceded by @required can safely be considered required. However, classes within a protocol that are preceded by neither are construed as being required. The reason is that until the addition of optional methods, all protocol methods were required. To avoid breaking code, that is the default interpretation for the compiler.

When working with optional methods, it is essential to check if they are implemented. The code for that is

if ([self.detailDelegate respondsToSelector:
  @selector(detailContentsDidChange:)])
  {...}

Going forward, it is better to specify which methods or properties are optional and which are required.

Summary

This hour has introduced protocols and delegates. Together, they let you assemble code that enhances the code that you write and that is inherited by your superclass. The structure can make for a simpler pattern than relying on subclassing objects.

Q&A

Q. How do you use a protocol to add properties to a class?

A. Declare the property in the protocol and add an @synthesize directive in the protocol adopter.

Q. What is a delegate and how is it related to protocols?

A. A delegate is a part of many Cocoa classes. It is designed to be implemented by a class that you create and assign to the delegate property of the class. Your class must respond to the messages that can be sent to the delegate. Those are usually specified in a protocol for that delegate.

Workshop

Quiz

1. If a protocol method is not marked as required or optional, how is it interpreted?

2. How many protocols can a class adopt?

Quiz Answers

1. If a protocol method is not marked as required or optional, it is interpreted as required.

2. There is no specified limit.

Activities

UITableView has both a delegate and a data source. Each has a protocol that it must implement. Why are they separate, particularly when you consider that frequently a subclass of UITableView is its own delegate and data source?

Hint: Look at the names of the methods in the two protocols. One manages the data and the other manages the appearance of the data in the interface.

The protocol would be declared and implemented in the master view controller, and it would contain the code to save the data. If a master view controller adopts that protocol, it will be expected to execute the code to manage the save when the detail view controller requests it via the protocol. This eliminates the need for the detail view controller to have a reference to the master view controller.

What it does have is a delegate that adopts the protocol. Via that delegate, it can request the save to occur. It doesn’t need to know what object is implementing the protocol because the delegate is set by the master view controller when it sets up the detail view controller. The detail view controller only knows that something implements the protocol.

Here are the steps to implement this scenario. It’s worth reviewing because the pattern is so common in Objective-C. (Note that although the steps are numbered, they can be implemented in any order. Until the last step—whatever it might be in your sequence of steps—one or more errors or warnings may appear.)

1. In the header of the class that will implement the protocol, declare it. In this case, you would add code such as that shown in Listing 17.1 at the top of MasterViewController.h before the @interface section.

LISTING 17.1 DetailDelegateProtocol Declaration


@protocol DetailDelegateProtocol <NSObject>
@required
- (void)detailContentsDidChange:(id)item;
@end


2. Specify that the master view controller will adopt the protocol with this code:

Click here to view code image

@interface MasterViewController : UITableViewController <DetailDelegateProtocol>

3. Implement the protocol code in MasterViewController.m

4. Set the delegate of the object that will call the protocol to the master view controller itself. For example, in the viewDidLoad method of MasterViewController, you could use this line of code.

Click here to view code image

self.detailViewController.delegate = self;

5. In DetailViewController.h, declare a forward reference to the protocol like this:

Click here to view code image

@protocol DetailDelegateProtocol;

6. Declare the delegate in the interface of DetailViewController.h:

Click here to view code image

@property (strong, nonatomic) id <DetailDelegateProtocol> delegate;

7. In DetailViewController.m, you can now use the protocol. For example, you could write this code:

Click here to view code image

[self.delegate detailContentsDidChange:self];

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

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