Protocols

Protocols

Every reasonably sophisticated object-oriented language must face the fact that the hierarchy of subclasses and superclasses is insufficient to express the desired relationships between classes. For example, a Bee object and a Bird object might need to have certain features in common by virtue of the fact that both a bee and a bird can fly. But Bee might inherit from Insect, and not every insect can fly, so how can Bee acquire the aspects of a Flier in a way that isn’t completely independent of how Bird acquires them?

Some object-oriented languages solve this problem through mixin classes. For example, in Ruby you could define a Flier module, complete with method definitions, and incorporate it into both Bee and Bird. Objective-C uses a simpler, lighter-weight approach — the protocol. Cocoa makes heavy use of protocols.

A protocol is just a named list of method declarations, with no implementation. A class may formally declare that itconforms to (oradopts) a protocol; such conformance is inherited by subclasses. This declaration satisfies the compiler when you try to senda corresponding message: if a protocol declares an instance method myCoolMethod, and if MyClass declares conformance to that protocol, then you can send the myCoolMethod message to a MyClass instance and the compiler won’t complain.

Actually implementing the methods declared in a protocol is up to the class that conforms to it. A protocol method may be required or optional. If a protocol method is required, then if a class conforms to that protocol, the compiler will warn if that class fails to implement that method. Implementing optional methods, on the other hand, is optional. (Of course, that’s just the compiler’s point of view; at runtime, if a message is sent to an object with no implementation for the corresponding method, a crash can result; see Chapter 3.)

Here’s an example of how Cocoa uses a protocol. Some objects can be copied; some can’t. This has nothing to do with an object’s class heritage. Yet we would like a uniform method to which any object thatcan be copied will respond. So Cocoa defines a protocol named NSCopying, which declares just one method, copyWithZone: (required). A class that explicitly conforms to NSCopying is promising that it implements copyWithZone:.

Here’s how the NSCopying protocol is defined (in NSObject.h, where your code can see it):

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end

That’s all there is to defining a protocol. The definition uses the @protocol compiler directive; it states the name of the protocol; it consists entirely of method declarations; and it is terminated by the @end compiler directive.

A protocol definition will typically appear in a header file, so that classes that need to know about it, in order to adopt it or call its methods, can import it. A protocol section of a header file is not inside any other section (such as an interface section).

Any optional methods must be preceded by the @optional directive. A protocol definition may state that the protocol incorporates other protocols; these constitute a comma-separated list in angle brackets after the protocol’s name, like this example from Apple’s own code (UIAlertView.h):

@protocol UIAlertViewDelegate 

@optional

- (void)alertView:(UIAlertView *)alertView

    clickedButtonAtIndex:(NSInteger)buttonIndex;

// ... more optional method declarations ...

@end

The NSCopying protocol definition in NSObject.h is just a definition; it is not a statement that NSObject conforms to NSCopying. Indeed, NSObject doesnot conform to NSCopying! To see this, try sending the copyWithZone: method to your own subclass of NSObject:

MyClass* mc = [MyClass new];

MyClass* mc2 = [mc copyWithZone: nil];

Under ARC, that code won’t compile, because no implementation of copyWithZone: has been inherited.

To conform formally to a protocol, a class’s interface section appends the name of the protocol, in angle brackets, after the name of the superclass (or, if this is a category declaration, after the parentheses). This will necessitate importing the header file that declares the protocol (or some header file that imports that header file). To state that a class conforms to multiple protocols, put multiple protocol names in the angle brackets, separated by comma.

Let’s see what happens if you conform formally to the NSCopying protocol. Modify the first line of the interface section of your class as follows:

@interface MyClass : NSObject 

Now your code compiles, but the compiler warns that MyClass fails to implement copyWithZone:, which it is contracted to do because copyWithZone: is a required method of the NSCopying protocol.

The name of a protocol may also be used when specifying an object type. Most often, the object will be typed as an id, but with the accompanying proviso that it conforms to a protocol, whose name appears in angle brackets.

To illustrate, let’s look at another typical example of how Cocoa uses protocols, namely in connection with a table view (UITableView). A UITableView has a dataSource property, declared like this:

@property (nonatomic, assign) id<UITableViewDataSource> dataSource

This property represents an instance variable whose type is id <UITableViewDataSource>. This means “I don’t care what class my data source belongs to, but whatever it is, it should conform to the UITableViewDataSource protocol.” Such conformance constitutes a promise that the data source will implement at least the required instance methods tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:, which the table view will call when it needs to know what data to display.

If you attempt to set a table view’s dataSource property to an object that doesnot conform to UITableViewDataSource, you’ll get a warning from the compiler. So, for example:

MyClass* mc = [MyClass new];

UITableView* tv = [UITableView new];

tv.dataSource = mc; // compiler warns

Under ARC, this warning is couched in rather confusing terms, along these lines: “Assigning to ‘id<UITableViewDataSource>’ from incompatible type ‘MyClass *__strong’.”

To quiet the compiler, MyClass’s declaration should state that it conforms to UITableViewDataSource. Once it does so, MyClassis an id <UITableViewDataSource>, and the third line no longer generates a warning. Of course, you must also supply implementations of tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: in MyClass to avoid the other warning, namely that you’re not implementing a required method of a protocol you’ve claimed to conform to.

In a very large percentage of cases, the object that you want to assign where conformity to a protocol is expected is self. In this situation, you can declare this class’s conformity to the protocol in the implementation (.m) file as part of a class extension, like this:

// MyClass.m:

@interface MyClass () <UITableViewDataSource>

@end


@implementation MyClass

- (void) someMethod {

    UITableView* tv = [UITableView new];

    tv.dataSource = self;

}

@end

I prefer this arrangement, because it means that the declaration of conformity to the protocol is right there in the same file that uses the protocol.

A prevalent use of protocols in Cocoa is in connection with delegate objects (and indeed, it is primarily in order to implement delegation that you are most likely to define your own protocols). We’ll talk in detail about delegates inChapter 11, but you can readily see that many classes have a delegate property and that the class of this property is often id <SomeProtocol>. For example, in our Empty Window project, the AppDelegate class provided by the project template is declared like this:

@interface AppDelegate : UIResponder <UIApplicationDelegate>

The reason is that AppDelegate’s purpose on earth is to serve as the shared application’s delegate. The shared application object is a UIApplication, and UIApplication’s delegate property is typed as an id <UIApplicationDelegate>. So AppDelegate announces its role by explicitly conforming to UIApplicationDelegate.

A slight chicken-and-egg problem arises in a header file containing a protocol definition and a class’s interface section, each of which mentions the other. The class’s interface section, it seems, cannot come first, because it mentions the protocol before the protocol has been defined; but the protocol definition can’t come first either, because it mentions the class before the class has been defined. The usual solution is to put the class’s interface section first, preceded by aforward declaration of the protocol, a single line introducing just the name of the protocol, whose definition will be forthcoming elsewhere — meaning, as it turns out, three lines from now:

@protocol MyProtocol;

@interface MyClass : NSObject

@property (nonatomic, weak) id<MyProtocol> delegate;

@end

@protocol MyProtocol

- (void) doSomething: (MyClass*) m;

@end

As a programmer, Cocoa’s use of protocols will matter to you in two ways:

Conformity

If an object value that you wish to assign or pass as an argument is typed as id <SomeProtocol>, you must make sure that that object’s class does indeed conform to SomeProtocol (and implements any methods required by that protocol).

Using the documentation

A protocol has its own documentation page. When the UIApplication class documentation tells you that the delegate property is typed as an id <UIApplicationDelegate>, it’s implicitly telling you that if you want to know what messages a UIApplication’s delegate might receive, you need to look in the UIApplicationDelegate protocol documentation.

Similarly, when a class’s documentation mentions that the class conforms to a protocol, don’t forget to examine that protocol’s documentation, because the latter might contain important information about how the class behaves. To learn what messages can be sent to an object, as I already mentioned in Chapter 8, you need to look upward through the superclass inheritance chain; you also need to look at any protocols that this object’s class (or superclass) conforms to.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值