A protocol is a list of method declarations that is not bound to any one class. The methods are not implemented in the protocol; instead, classes conform to or adopt a protocol and within those classes the methods are implemented. A protocol is simply a grouping of certain related methods under one name, and it acts like a contract—conforming classes must implement the required methods (and may implement any of the potential optional methods).
The syntax for declaring a protocol is simple:
- (id)copyWithZone:(NSZone *)zone;
The text in bold is required syntax. The name of the protocol is written in italics. Between the mandatory lines is the method(s) of the protocol, declared as you would normally declare methods. The protocol is typically saved in a .h (header) file, and follow the same naming conventions as classes do.
For a class to adopt one or more protocols, place the protocol names between angle brackets, separated by commas if there is more than one. A class that adopts the
NSCopying protocol might be declared like this:
@interface CopyMachine : NSObject <NSCopying, NSCoding>
The compiler sees that the CopyMachine class conforms to the NSCopying and NSCoding protocols, and it will issue a warning if the required methods in those classes are not implemented. Note also that protocol conformity is inherited—subclasses of CopyMachine, for example, will also conform to those protocols, and can re-implement (override) those methods as necessary.
Let’s look at the theory behind protocols.
Apple’s documentation on protocols states that there are at least three reasons to use protocols:
- To declare methods for others to implement
- To declare the public interface to a class while hiding the nature of the class itself
- To mark similarities between classes that are not part of the same inheritance tree
In fact, there is a fourth reason as well, and in everyday use it is the most compelling: It lets you avoid the complexities of subclassing, similar to the way categories do, but in a different manner.
Protocols play an important role in OS X and iOS development—a lot of built-in classes use them. A classic example is the UITableViewController, which is a view controller that manages a table view (basically a scrollable list, like in the iPod application). Without protocols, users of the class would have to subclass the original class, deal with any issues, and override the original methods. This is another challenge—for the original class. For something like a table view, where the data for the view may come from a variety of locations and there may be a myriad of behaviors to handle, what is an acceptable default implementation? Using protocols circumvents the issue. UIKit also includes two protocols, designed to work with UITableViewControllers. They are UITableViewDataSource and UITableViewDelegate. Any class that adopts these protocols will be able to control a table view.
Apple’s documentation on protocols effectively describes the other reasons. Let’s return to the syntax.
Other Protocol Functionality
Methods in a protocol can be declared to be optional, by using the @optional directive. This works like the ones for instance variable scope—it acts like a switch. The opposite is @required.
You can check to see if a class implements a protocol by calling the conformsToProtocol: method. The single argument is of type Protocol, declared with the @protocol (protocolName) directive.
if ([someObject conformsToProtocol:@protocol(NSCopying)]) …
The id type is designed to be for any object type, but you can use the compiler to ensure that only objects that conform to a protocol are assigned to the object. Note that this only works with a statically type object being assigned to the id type, as it is a compiler check. This type of usage is often found when classes have an ivar or property for their delegate, which is an id type that conforms to a corresponding delegate protocol. The syntax looks like this:
id <NSCopying> delegate;
Remember that this is a compiler check, so it only works with statically typed variables and objects.
Java programmers may recognize the similarity between protocols and Java’s interfaces. In fact, the former was the inspiration for the latter. The main benefit of interfaces in Java is so that a collection can hold a number of objects of different classes, which could all be upcast to a shared interface—an object could implement many interfaces. Because NSArray can already hold objects of different types without limitations, this is not the intention of protocols in Objective-C. Rather, protocols are used extensively in Cocoa as a communications medium. This best explained by Apple’s documentation on delegation:
“A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object—that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit—that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner.
To better appreciate the value of delegation, it helps to consider an off-the-shelf Cocoa object such as a text field (an instance of NSTextField or UITextField) or a table view (an instance of NSTableView or UITableView ). These objects are designed to fulfill a specific role in a generic fashion; a window object in the AppKit framework, for example, responds to mouse manipulations of its controls and handles such things as closing, resizing, and moving the physical window. This restricted and generic behavior necessarily limits what the object can know about how an event affects (or will affect) something elsewhere in the application, especially when the affected behavior is specific to your application. Delegation provides a way for your custom object to communicate application-specific behavior to the off-the-shelf object.
The programming mechanism of delegation gives objects a chance to coordinate their appearance and state with changes occurring elsewhere in a program, changes usually brought about by user actions. More importantly, delegation makes it possible for one object to alter the behavior of another object without the need to inherit from it. The delegate is almost always one of your custom objects, and by definition it incorporates application-specific logic that the generic and delegating object cannot possibly know itself.”
This post is part of the Learn Objective-C in 24 Days course.
Author: Feifan Zhou