delegate初探
在ios开发中,我们经常会用到类似如下的对话框:
因此,如下这段代码我们也就很熟悉了:
- (IBAction)showSheet:(id)sender {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:@"title,nil时不显示"
delegate:self
cancelButtonTitle:@"取消"
destructiveButtonTitle:@"确定"
otherButtonTitles:@"第一项", @"第二项",nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[actionSheet showInView:self.view];
}
其中initWithTitle函数的第二个参数为delegate,那么是什么呢? 我们到它的头文件中看看。initWithTitle这个函数的声明如下 :
- (id)initWithTitle:(NSString *)title delegate:(id<UIActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
是的,上面这个巨长无比的函数声明就是initWithTitile函数,oc这个语言本身给我的感觉就是繁杂。废话不多说,我们直接看到delegate参数的类型是id<UIActionSheetDelegate>,直接看UIActionSheetDelegate的声明:
@protocol UIActionSheetDelegate <NSObject>
@optional
// Called when a button is clicked. The view will be automatically dismissed after this call returns
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex;
// Called when we cancel a view (eg. the user clicks the Home button). This is not called when the user clicks the cancel button.
// If not defined in the delegate, we simulate a click in the cancel button
- (void)actionSheetCancel:(UIActionSheet *)actionSheet;
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet; // before animation and showing view
- (void)didPresentActionSheet:(UIActionSheet *)actionSheet; // after animation
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex; // before animation and hiding view
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex; // after animation
@end
可以看到UIActionSheetDelegate是一个普普通通的协议,在@optional下面有六个函数,这些函数都是可选实现的,每个函数对应的是UIActionSheet中每个按钮的点击事件处理,当然最后两个函数是按照索引来区分Button对象的。
delagate的实现
协议与delegate不是同一概念,协议是语言级别的特性,而delegate是借助协议来的实现的一种设计模式,其实就是代理模式。通过注入
代理对象实现相应的功能。其实它的主要功能也就是实现回调,与Java中的listener一样。
下面以一个示例来说明 :
// ButtonClickDelegate协议
@protocol ButtonClickDelegate <NSObject>
-(void) onClick: (id) sender ;
@end
// view的声明,实现了ButtonClickDelegate协议
@interface UIView : NSObject <ButtonClickDelegate >
{
@protected id<ButtonClickDelegate> clickDelegate ;
}
// 点击事件代理,因此UIView实现了ButtonClickDelegate协议,因此自己可以为自己代理。
@property (nonatomic, strong) id<ButtonClickDelegate> clickDelegate ;
// 点击view的触发函数
-(void) performClick ;
@end
// view的实现
@implementation UIView
@synthesize clickDelegate ;
// 默认的点击事件
-(id) init
{
self = [super init] ;
if ( self ) {
clickDelegate = self ;
}
return self;
}
// 点击view的事件的默认处理
-(void) onClick: (id) sender
{
NSLog(@"点击view的默认处理函数.") ;
}
// 点击事件
-(void) performClick
{
[clickDelegate onClick: self ] ;
}
@end
// ViewController声明, 实现了ButtonClickDelegate协议,可以作为UIView的代理
@interface ViewController : NSObject <ButtonClickDelegate>
@property (nonatomic, strong) UIView* parenView ;
@end
// ViewController实现
@implementation ViewController
-(void) onClick:(id)sender
{
NSLog(@"ViewController来实现点击事件") ;
}
@end
main函数:
// main
int main(int argc, const char * argv[])
{
@autoreleasepool {
// view对象
UIView* view = [[UIView alloc] init] ;
[view performClick] ;
// 构建一个ViewController对象
ViewController* controller = [[ViewController alloc] init] ;
view.clickDelegate = controller ;
[view performClick] ;
}
return 0;
}
首先创建了一个UIView对象view, 然后调用performClick函数,此时view没有设置delegate,但是由于自己实现了ButtonClickDelegate协议,因此可以为自己代理该点击事件。然后我们创建了ViewController对象controller, 并且将该对象设置为view对象的delegate, 然后执行performClick函数,此时在performClick函数中
会执行ViewController中的onClick函数,即controller代理了view的点击事件处理。输出结果如下 :
点击view的默认处理函数.
ViewController来实现点击事件
delegate与Java中的Listener
delegate与Java中的Listener的功能大致是相同的,比如我们看看Android中一个按钮的点击事件的处理。
Button mJumpButton = (Button) findViewById(R.id.button_jump);
mJumpButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
SecondActivity.class);
startActivity(intent);
}
});
其中的Button就相当于上文中的UIView,而其setOnClickListener就相当于delegate属性的设置方法,OnClickListener就扮演了上文中ButtonClickDelegate的角色,
onClick方法更是如出一辙。其实每个平台大体上的设计思路也都是很相近的,观察不同平台的对比实现更容易理解吧。
delegate与id类型
// 点击事件代理,因此UIView实现了ButtonClickDelegate协议,因此自己可以为自己代理。
@property (nonatomic, strong) id<ButtonClickDelegate> clickDelegate ;
注意这里的类型是id<ButtonClickDelegate>;这表明该delegate对象可以是任意类型,但是该类型必须实现ButtonClickDelegate协议,也可以说成该类型必须采用正式协议ButtonClickDelegate。这个就很像Java中的泛型,例如我们可以在Java中这样使用泛型,
void setData(List<? extends Order> myorders) ;
在setData函数中接受的参数为元素类型为Order子类的List集合,与id<ButtonClickDelegate>是不是又很相似呢?