1、委托设计模式
委托是为了降低一个对象的复杂度和耦合度,使其能够更具有通用性而将其中一些处理置于委托对象中的编码方式。通用类因为通用性而变为框架类(AppDelete类),而框架类保持委托对象的指针,并在特定时刻向委托对象发送消息,消息可能只是通知委托做些事情,也可能是对委托对象进行控制。下面结合一个实际例子,来介绍这个委托机制。
2、实现原理
设计模式的类图如下图所示:
有个哲学家,他只做3件事:“睡觉”、“吃饭”、“工作”。为了更好滴生活,提高工作效率,他会找一个徒弟,把这些事情委托给徒弟去做。然而要成为他的徒弟,需要实现一个协议,协议要求能够处理“睡觉”、“吃饭”、“工作”。三者关系如图所示:
从图中可以看出,哲学家类保持指向委托对象(ViewController)的“弱引用”id<PhilosopherDelegate> delegate,委托对象ViewController就是哲学家的“徒弟”,它实现了协议PhilosopherDelegate,PhilosopherDelegate规定了3个方法:-(void) sleep、-(void) eat和
-(void) work方法。对应的实例代码如下:
@protocol PhilosopherDelegate
@required
-(void) sleep;
-(void) eat;
-(void) work;
@end
可以看到,委托协议PhilosopherDelegate定义了3个方法。如果该委托协议没有.m文件,它的定义可以放在别的.h文件中,而不必单独定义在.h文件中。它的实现类就是委托类ViewController,相关代码如下:
//
// ViewController.h
//
@interface ViewController : UIViewController<PhilosopherDelegate>
@end
//
// ViewController.m
//
- (void)viewDidLoad
{
[super viewDidLoad];
Philosopher *obj = [[Philosopher alloc ] init];
obj.delegate = self;
[obj start];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma -- PhilosopherDelegate 方法实现
-(void) sleep
{
NSLog(@"sleep...");
}
-(void) eat
{
NSLog(@"eat...");
}
-(void) work
{
NSLog(@"work...");
}
委托对象如何与通用类之间建立引用关系呢?我们通过 obj.delegate = self;语句来指定对象和通用类之间引用关系。一般情况下,通用类由框架直接提供。在这个例子中,我们自己实现了通用类Philosopher。 Philosopher代码如下:
//
// Philosopher.h
//
#import "PhilosopherDelegate.h"
@interface Philosopher : NSObject
{
NSTimer *timer;
int count;
}
@property (nonatomic, weak) id<PhilosopherDelegate> delegate;
-(void) start;
-(void) handle;
@end
上述代码中我们定义了delegate属性,它的类型是<PhilosopherDelegate>,它可以保存委托对象的引用,Philosopher.m代码如下:
//
// Philosopher.m
//
#import "Philosopher.h"
@implementation Philosopher
@synthesize delegate;
-(void) start
{
count= 0;
timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(handle)userInfo:nil repeats:YES];
}
-(void)handle
{
switch (count)
{
case 0:
[self.delegate sleep];
count++;
break;
case 1:
[self.delegate eat];
count++;
break;
case 2:
[self.delegate work];
[timer invalidate];
break;
}
}
@end
在 Philosopher.m中,模拟一些通用类发出的调用,这个调用通过NSTimer没3秒发出一个,依次向委托发出消息sleep、eat、和work。self.delegate是指向委托对象ViewController的指针,[self.delegate work];是调用Viewcontroller中的sleep方法。
3、应用场景
3-1、委托机制应用在开发中应用极其广泛,拿UITextFieldDelegate为例说明一下,UITextFieldDelegate是控制UITextField类的委托。它主要负责响应控件事件或控制其他对象。打开其API文档就可以发现,其中有以下方法:
@protocol UITextFieldDelegate <NSObject>
@optional
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField; // return NO to disallow editing.
- (void)textFieldDidBeginEditing:(UITextField *)textField; // became first responder
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField; // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
- (void)textFieldDidEndEditing:(UITextField *)textField; // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; // return NO to not change text
- (BOOL)textFieldShouldClear:(UITextField *)textField; // called when clear button pressed. return NO to ignore (no notifications)
- (BOOL)textFieldShouldReturn:(UITextField *)textField; // called when 'return' key pressed. return NO to ignore.
@end
而在UITextField类中,看到有以下引用指向UITextFieldDelegate委托的引用。
@property(nonatomic) BOOL adjustsFontSizeToFitWidth; // default is NO. if YES, text will shrink to minFontSize along baseline
@property(nonatomic) CGFloat minimumFontSize; // default is 0.0. actual min may be pinned to something readable. used if adjustsFontSizeToFitWidth is YES
@property(nonatomic,assign) id<UITextFieldDelegate> delegate; // default is nil. weak reference
@property(nonatomic,retain) UIImage *background; // default is nil. draw in border rect. image should be stretchable
@property(nonatomic,retain) UIImage *disabledBackground; // default is nil. ignored if background not set. image should be stretchable
@property(nonatomic,readonly,getter=isEditing) BOOL editing;
@property(nonatomic) BOOL allowsEditingTextAttributes NS_AVAILABLE_IOS(6_0); // default is NO. allows editing text attributes with style operations and pasting rich text
@property(nonatomic,copy) NSDictionary *typingAttributes NS_AVAILABLE_IOS(6_0); // automatically resets when the selection changes
3-2、委托机制在实际的开发中,我个人用的最多的还是用来进行“值的回传”,页面传递消息时,用委托十分的有效。我曾经写过一个自定义相机的类CameraViewController,这个类要拍摄并生成一张图片,并传给需要这张图片的类。当时有很多类(A、B、C等等)需要这张图片,
于是我在CameraViewController类中定义了一个协议:主要就是将CameraViewController类中的生成的图片传出去,大致的定义如下:
//
// CameraViewController.h
//
@class CameraViewController;
@protocol PhotoDelegate<NSObject>
@optional
- (void)getPhotoFromCustomCamera:(CameraViewController *)picker withImage: (UIImage *)wImage;
@end
并保留了此协议的引用:
@property (assign, nonatomic) id<PhotoDelegate> delegate;
在CameraViewController.m文件中,我在适当的时机将此图片传出去了:
if (self.delegate && [self.delegate respondsToSelector:@selector(getPhotoFromCustomCamera:withImage:)])
{
[self.delegate getPhotoFromCustomCamera:self withImage:self.targetImage];
}
而在A、B、C等调用类中需要指定引用类CameraViewController与委托对象(A、B、C等调用类)直接的引用关系
CameraViewController *wCV = [[CameraViewController alloc]initWithNibName:@"CameraViewController" bundle:nil];
wCV.delegate = self;
[[AppController instance] presentModalViewController:wCV animated:YES];
而在A、B、C等调用类中只需要继承协议,实现协议方法即可:
- (void)getPhotoFromCustomCamera:(CameraViewController *)picker withImage: (UIImage *)wImage
{
self.imageFromCustomCamera = wImage;
//将拍照后的图片保存到本地相册
UIImageWriteToSavedPhotosAlbum(self.imageFromCustomCamera, self, nil, nil);
}
这样在你调用CameraViewController类生成一张照片时,在CameraViewController类中就可以将图片回传给需要它的地方。
4、参考资料
《ios开发指南》 ----关东升