iOS开发中,一个比较重要的知识点是不同的控制器之间,MVC之间如何相互传值,如何让其中一个类监听另外一个类的状态。cocoa提供了几种方式:Delegate,Notification Center,Block和KVO。
为什么我们需要这些消息传递机制?以及它们之间的区别是什么?总的来说,我们经常需要一个对象来监听另外一个对象的事件,同时又希望代码之间不要有太高的耦合度,所以合适地使用这三种设计模式,有助于写出可复用以及优雅的代码。
1:Delegation
在苹果的框架里面,Delegation几乎随处可见。它让我们能自定义对象的行为,并收到一些触发的事件。举例一个最常见的例子:UITableViewDelegate
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
@optional
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
// ...省略
@end
如果我希望某个ViewController可以有能力接受TableView的委任,假设UITableView是设定成一个叫做tableView的IBOutlet的话,我們可以直接在ViewDidLoad的地方这样写:
- (void)viewDidLoad{
[super viewDidLoad];
_tableView.delegate = self;
}
在storyBoard中处理也一样。
并且,不要忘了
@interface ViewController()<UITableViewDelegate>
这样之后,view就可以发送通知给controller了:
tableview对viewController说:"我里面的cell就要出现了,你可以在这个时候做点什么"
view controller 回答说:"那就把cell都打勾吧"
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
cell.accessoryType =UITableViewCellAccessoryCheckmark;
}
那如何写一个delegete?我们举一个例子来说明:
我们先新建一个自定义view:
CustomView.h
#import <UIKit/UIKit.h>
@interface CustomView : UIView
@end
CustomView.m
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor lightGrayColor];
UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width/2, self.frame.size.height)];
button.backgroundColor = [UIColor blackColor];
[button addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
当我们需要监听CustomView上button的点击事件时,我们可以声明一个delegate:
CustomView.h
@class CustomView;
@protocol CustomViewDelegate <NSObject>
@optional//可以选择将其声明为@required
- (void) CustomView:(CustomView *)CustomView didClickButton:(UIButton*)button;
@end
@interface CustomView : UIView
@property (nonatomic,weak) id<CustomViewDelegate>delegate;
@end
CustomView.m
-(void)click:(UIButton *)sender{
if ([self.delegate respondsToSelector:@selector(CustomView:didClickButton:)]) {
[self.delegate CustomView:self didClickButton:sender];
}
}
这样在我们的控制器上只要遵守CustomViewDelegate,就可以监听CustomView上button的点击事件了:
ViewController.m
@interface ViewController ()<CustomViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CustomView *customView = [[CustomView alloc]initWithFrame:CGRectMake(100, 100, 150, 50)];
customView.delegate = self;
[self.view addSubview:customView];
}
-(void)CustomView:(CustomView *)CustomView didClickButton:(UIButton *)button{
NSLog(@"delegate--click!");
}
@end
因为在自定义的view上的点击事件只有view可以监听得到(通过addTarger或者IBAction),但是有时候我们需要在控制器中处理事件逻辑,这时候我们可以写一个Delegation把点击事件传递给控制器。
因为delegate协议可以定义任何的方法,我们可以照着自己的需求来传递消息。可以用方法参数来传递消息内容,例如传递一个BOOL值:
-(void)CustomView:(CustomView *)CustomView ButtonClick:(UIButton *)Button CustomViewShow:(BOOL)CustomViewShow;
delegate 可以通过返回值的形式来给发送者作出回应:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 22;
}
如果只要在相对接近的两个模块间传递消息,delgation可以灵活很直接的消息传递机制。
2.Notification
NSNotificationCenter是一个单例。要在代码中的两个不相关的模块中传递消息时,通知机制是非常好的工具。通知机制广播消息,当消息内容丰富而且无需指望接收者一定要关注的话这一招特别有用。
比如我们可以接收一个UIKeyboardWillShowNotification来监听键盘的出现:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(inputKeyboardShow:)
name:UIKeyboardWillShowNotification
object:nil];
当然不要忘了
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
}
发送通知应该怎么写?同样在CustomView中举例,这次我们通过一个userinfo来传值
CustomView.m
-(void)click:(UIButton *)sender{
NSDictionary *dic = [NSDictionary dictionaryWithObject:@"click" forKey:@"buttonKey"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"buttonClick" object:self userInfo:dic];
}
ViewController.m中接收消息并在userinfo中解析字典值
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(click:) name:@"buttonClick" object:nil];
}
-(void)click:(NSNotification*)notification{
NSLog(@"notification--%@",[notification.userInfo objectForKey:@"buttonKey"]);
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self name:@"buttonClick" object:nil];
}
通知可以用来发送任意消息,甚至可以包含一个userInfo字典。通知的独特之处在于,发送者和接收者不需要相互知道对方,所以通知可以被用来在不同的相隔很远的模块之间传递消息。这就意味着这种消息传递是单向的,我们不能回复一个通知。
网友评论