Delegation

94 篇文章 0 订阅
6 篇文章 0 订阅
Delegation:


Delegation is an object-oriented design pattern, a relationship between two objects, in which the first object’s behavior is customized or assisted by the second. The second object is the first object’s delegate. No subclassing is involved, and indeed the first object is agnostic about the second object’s class.
As implemented by Cocoa, here’s how delegation works. A built-in Cocoa class has an instance variable, usually called delegate (it will certainly have delegate in its name). For some instance of that Cocoa class, you set the value of this instance variable to an instance of one of your classes. At certain moments in its activity, the Cocoa class promises to turn to its delegate for instructions by sending it a certain message: if the Cocoa instance finds that its delegate is not nil, and that its delegate is prepared to receive that message (see Chapter 10 on respondsToSelector:), the Cocoa instance sends the message to the delegate.
Recall the discussion of protocols from Chapter 10. Delegation is one of Cocoa’s main uses of protocols. In the old days, delegate methods were listed in the Cocoa class’s documentation, and their method signatures were made known to the compiler through an informal protocol (a category on NSObject). Now, though, a class’s delegate methods are usually listed in a genuine protocol with its own documentation. There are over 70 Cocoa delegate protocols, showing how heavily Cocoa relies on delegation. Most delegate methods are optional, but in a few cases you’ll discover some that are required.




Cocoa Delegation
To customize a Cocoa instance’s behavior through delegation, you start with one of your classes, which, if necessary, declares conformance to the relevant delegate protocol. When the app runs, you set the Cocoa instance’s delegate property (or whatever its name is) to an instance of your class. You might do this in code; alternatively, you might do it in a nib, by connecting an instance’s delegate outlet (or whatever it’s called) to an appropriate instance that is to serve as delegate.




- (void) gameWon {
    UIAlertView* av =
        [[UIAlertView alloc] initWithTitle:@"Congratulations!"
                                   message:@"You won the game. Another game?"
                                  delegate:self
                         cancelButtonTitle:@"No, thanks."
                         otherButtonTitles:@"Sure!", nil];
    [av show];
}


- (void) alertView:(UIAlertView*) av
        didDismissWithButtonIndex: (NSInteger) ix {
    if (ix == 1) { // user said "Sure!"
        [self newGame];
    }
}






int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil,
                                 NSStringFromClass([AppDelegate class]));
    }
}

The template has provided the project with files defining an AppDelegate class; that line tells UIApplicationMain to instantiate AppDelegate and make that instance the delegate of the shared application instance (which it has also created). As I pointed out in Chapter 10, AppDelegate formally adopts the UIApplicationDelegate protocol, signifying that it is ready to serve in this role. And, as I also said in Chapter 10, respondsToSelector: is then sent to the app delegate to see what UIApplicationDelegate protocol methods it implements. If it implements application:didFinishLaunchingWithOptions:, it will be sent application:didFinishLaunchingWithOptions:, which is thus one of the earliest opportunities for your code to run.



By convention, many Cocoa delegate method names contain the modal verbs should, will, or did. A will message is sent to the delegate just before something happens; a did message is sent to the delegate just after something happens. A should method is special: it returns a BOOL, and you are expected to respond with YES to permit something or NO to prevent it. The documentation tells you what the default response is; you don’t have to implement a should method if the default response is always acceptable.




Implementing Delegation


The Cocoa pattern of a delegate whose responsibilities are described by a protocol is one that you will want to imitate in your own code. Setting up this pattern takes some practice, and can be a little time-consuming, but it is often the most appropriate approach, because of the way it assigns knowledge and responsibility to the various objects involved. A very typical situation is a case of two-way communication: for example, Object A creates and configures Object B; later, before Object B goes out of existence, Object A also needs to hear back from Object B.

The pattern here is that Class B defines a protocol SomeProtocol in its header, along with a delegate property typed as id <SomeProtocol>. That way, Class A imports Class B’s header, which is right and proper because Object A is going to create Object B and configure it, but Class B needn’t know anything about Class A, which is right and proper because it’s just a servant, and should be able to serveany other object. Because Class A imports Class B’s header, it knows about SomeProtocol and can adopt it and implement its required methods. When Object A creates Object B, it also sets itself as Object B’s delegate. Now Object B knows all that it needs to know: it can send SomeProtocol messages to its delegate, regardless of that delegate’s actual class.

To see why this pattern is a good one, consider an actual case. In one of my apps I present a view where the user can move three sliders to choose a color. Appropriately, its code is in a class called ColorPickerController. When the user taps Done or Cancel, the view should be dismissed; but first, the code that presented this view needs to hear about what color the user chose. So I need to send a message from the ColorPickerController instance back to the instance that presented it. Here is the declaration for that message:

- (void) colorPicker:(ColorPickerController*)picker

    didSetColorNamed:(NSString*)theName

             toColor:(UIColor*)theColor;

The question is: where should this declaration go?

Now, it happens that in my app I know the class of the instance that will in fact present the ColorPickerController’s view: it is a SettingsController. So I could simply declare this method in the interface section of SettingsController’s header file, and have ColorPickerController import SettingsController’s header file. But this feels wrong:

  • It should not be up to SettingsController to declare a method that it is implementing only in deference to ColorPickerController.
  • If SettingsController declares this message in its header file, ColorPickerController will have to import that header file in order to send the message; but this means that ColorPickerController now knows all about SettingsController, whereas the only thing it needs to know about SettingsController is that it implements this one method.
  • It is merely a contingent fact that the instance being sent this messageis a SettingsController; it should be open toany class to present and dismiss a ColorPickerController’s view, and thus to be eligible to receive this message.

Therefore we want ColorPickerController itself to declare the method thatit itself is going to call; and we want it to send the message blindly to some receiver, without regard to the class of that receiver.Thus there needs to be a linkage, as it were, between the declaration of this method in ColorPickerController and the implementation of this method in the receiver. That linkage is precisely what a protocol creates!The solution, then, is for ColorPickerController to define a protocol in its header file, with this method as part of that protocol, and for the class that presents a ColorPickerController’s view to conform to that protocol. ColorPickerController also has an appropriately typed delegate property; this provides the channel of communication, and tells the compiler that sending this message is legal.

Here’s ColorPickerController’s header file (note the use of the forward declaration I mentioned inChapter 10):

@protocol ColorPickerDelegate;

@interface ColorPickerController : UIViewController

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

@end

@protocol ColorPickerDelegate

// color == nil on cancel

- (void) colorPicker:(ColorPickerController *)picker

    didSetColorNamed:(NSString *)theName

             toColor:(UIColor*)theColor;

@end

When my SettingsController instance creates and configures a ColorPickerController instance, it also sets itself as that ColorPickerController’s delegate. Now, when the user picks a color, the ColorPickerController knows to whom it should send colorPicker:didSetColorNamed:toColor:, namely, its delegate; and the compiler allows this, because the delegate has adopted the ColorPickerDelegate protocol:

- (void) dismissColorPicker: (id) sender { // user has tapped our Done button

    [self.delegate colorPicker:self

              didSetColorNamed:self.colorName

                       toColor:self.color];

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值