一、 KVO、通知、代理传值、block传值的实现过程
四个都是通过登陆注册的简单demo进行完成
一、Block传值
操作方法
1 为block取别名,并且在参数列表中将需要传递的参数写成形参 设置block属性(注意使用copy)
2 设置⼀个⽅法(比如按钮点击事件的方法)持有当前block 持有时将需要传递的值传递进去
3 在创建该对象的地方进行block⽅面的调用
源代码
//在需要传值的第二界面的.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RegisterViewController : UIViewController
@property (nonatomic, strong) UITextField *renameTextField;
@property (nonatomic, strong) UITextField *repassTextField;
@property (nonatomic, strong) UIButton *relandButton;
@property (nonatomic, copy) void (^testBlock) (NSString *name, NSString *pass);
@end
NS_ASSUME_NONNULL_END
//第二界面.m文件 上面的viewDidLoad中间的内容就是创建视图的位置 后面的按钮点击事件的方法则持有block并且负责赋值
-(void)back{
_testBlock(_renameTextField.text,_repassTextField.text);
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
//第一界面的viewcontroller负责在创建该对象的地方进行调用
-(void)press{
RegisterViewController *view = [[RegisterViewController alloc]init];
view.testBlock = ^(NSString *name,NSString *pass){
self->_nameTextField.text = name;
self->_passTextField.text = pass;
};
[self presentViewController:view animated:YES completion:nil];
}
二、代理传值
1.在第二级控制器.h文件中,设置非正式协议
2.在第二级控制器.h文件中写属性和协议中的方法
3.在第二级控制器.m文件中,利用代理属性,去调用协议里面的方法(同时把参数传出来)
4.在第一级控制器中遵守协议
5.在第一级控制器中设置代理
6.在第一级控制器中重写代理方法
//二级控制器.h文件
#import <UIKit/UIKit.h>
//第一步设置协议 protocol就是协议的意思 p2:自己设置这个协议的名字 p3:后面挂个NSObject
@protocol TestDelegate <NSObject>
-(void)pass:(NSString *_Nullable)name and:(NSString *_Nullable)pass;
@end
NS_ASSUME_NONNULL_BEGIN
@interface RegisterViewController : UIViewController
@property (nonatomic, strong) UITextField *renameTextField;
@property (nonatomic, strong) UITextField *repassTextField;
@property (nonatomic, strong) UIButton *relandButton;
@property (nonatomic, weak) id <TestDelegate> registerDelegate;
@end
NS_ASSUME_NONNULL_END
//二级控制器.m文件 在点击事件中,利用代理属性给协议里的方法两个参数赋值
-(void)back{
[_registerDelegate pass:_renameTextField.text and:_repassTextField.text];
[self dismissViewControllerAnimated:YES completion:nil];
}
//一级控制器先要设置代理 然后通过代理中的方法进行参数值的传递
//直接调用方法进行赋值即可
-(void)pass:(NSString *)name and:(NSString *)pass{
_nameTextField.text = name;
_passTextField.text = pass;
}
-(void)press{
RegisterViewController *view = [[RegisterViewController alloc]init];
//设置代理
view.registerDelegate = self;
[self presentViewController:view animated:YES completion:nil];
}
三、通知传值
1 在第二级控制器.m文件中 在按钮点击事件中创建通知对象 将注册于登陆的两个数值传入 然后去消息中心发通知
2 在第一级控制器.m文件中 在viewWillAppear中注册通知(添加观察者来指定一个方法、名称或对象,接受到通知时执行这个指定的方法。)
3 在调用方法中进行数据的赋值
4 移除观察者(为什么要移除观察者?)
//第二级视图控制器.m文件
-(void)back{
//1.创建通知对象
NSNotification *noti = [NSNotification notificationWithName:@"send" object:self userInfo:@{@"name":_renameTextField.text,@"pass":_repassTextField.text}];
//2.通知中心去发布通知
[[NSNotificationCenter defaultCenter] postNotification:noti];
[self dismissViewControllerAnimated:YES completion:nil];
}
//第一级控制器的.m文件
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//3.注册通知 添加观察者来指定一个方法、名称和对象,接受到通知时执行这个指定的方法。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(recive:) name:@"send" object:nil];
}
//4.接收通知后调用的方法
- (void)recive:(NSNotification *)noti {
NSDictionary *dic = noti.userInfo;
_nameTextField.text = dic[@"name"];
_passTextField.text = dic[@"pass"];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
四、KVO
注册观察者
使用方法:addObserver:forKeyPath:options:context:
参数含义:
1.observer:观察者,监听属性变化的对象。 该对象必须实现 observeValueForKeyPath:ofObject:change:context: 方法。
2. keyPath:要观察的属性名称。要和属性声明的名称一致。
3. options:对KVO机制进行配置,修改KVO通知的时机以及通知的内容
4. context: 传入任意类型的对象,在"接收消息回调"的代码中可以接收到这个对象,是KVO中的一种传值方式。
options参数:
enum {
NSKeyValueObservingOptionNew = 0x01,
NSKeyValueObservingOptionOld = 0x02,
NSKeyValueObservingOptionInitial = 0x04,
NSKeyValueObservingOptionPrior = 0x08
};
typedef NSUInteger NSKeyValueObservingOptions;
默认只接受新值
NSKeyValueObservingOptionNew:接收方法中使用change参数传入变化后的新值,键为:NSKeyValueChangeNewKey;
NSKeyValueObservingOptionOld:接收方法中使用change参数传入变化前的旧值,键为:NSKeyValueChangeOldKey;
NSKeyValueObservingOptionInitial:注册之后立刻调用接收方法,如果配置了NSKeyValueObservingOptionNew,change参数内容会包含新值,键为:NSKeyValueChangeNewKey;
NSKeyValueObservingOptionPrior:如果加入这个参数,接收方法会在变化前后分别调用一次,共两次,变化前的通知change参数包含notificationIsPrior = 1。
接收通知
使用方法:observeValueForKeyPath:ofObject:change:context:
取消注册
使用方法:removeObserver:forKeyPath:context
具体部分代码:
//第一级页面.m文件添加一个指向第二级视图的成员变量
@property (nonatomic, strong) RegisterViewController *secondView;
//按下按钮跳转界面添加下面
-(void)press{
_secondView = [[RegisterViewController alloc]init];
[_secondView addObserver:self forKeyPath:@"strNameWord" options:NSKeyValueObservingOptionNew context:nil];
[_secondView addObserver:self forKeyPath:@"strPassWord" options:NSKeyValueObservingOptionNew context:nil];
[self presentViewController:_secondView animated:YES completion:nil];
}
//接收与移除
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"strNameWord"]) {
_nameTextField.text = _secondView.strNameWord;
}
if ([keyPath isEqualToString:@"strPassWord"]) {
_passTextField.text = _secondView.strPassWord;
}
}
- (void)dealloc
{
[_secondView removeObserver:self forKeyPath:@"userName"];
[_secondView removeObserver:self forKeyPath:@"userPass"];
}
//第二级视图.h
//添加两个被观察的对象
@property (nonatomic, strong) NSString *strNameWord;
@property (nonatomic, strong) NSString *strPassWord;
//下面为正常的三个
@property (nonatomic, strong) UITextField *renameTextField;
@property (nonatomic, strong) UITextField *repassTextField;
@property (nonatomic, strong) UIButton *relandButton;
//按下时跳转页面并且修改值就行了
-(void)back{
self.strNameWord = self.renameTextField.text;
self.strPassWord = self.repassTextField.text;
[self dismissViewControllerAnimated:YES completion:nil];
}
以上过程有的不懂得地方参考自此篇博客:(iOS页面间传值的方式)
二、几种传值的比较
通过这个demo的实现了4种传值方式 简单总结:
1. 代理和通知的区别
效率:代理比通知高;
关联:delegate是强关联,委托和代理双方互相知道。通知是弱关联,不需要知道是谁发,也不需要知道是谁接收。
代理是一对一的关系,通知是一对多的关系。delegate一般是行为需要别人来完成。通知是全局通知。
代理要实现对多个类发出消息可以通过将代理者添加入集合类后遍历,或通过消息转发来实现。
2. KVO和通知的区别
相同:都是一对多的关系;
不同:通知是需要被观察者先主动发出通知,观察者注册监听再响应,比KVO多了发送通知这一步。
监听范围:KVO是监听一个值的变化。通知不局限于监听属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用更灵活。
使用场景:KVO的一般使用场景是监听数据变化,通知是全局通知。
3. block和代理的区别
相同点:block和代理都是回调的方式。使用场景相同。
不同点:
block集中代码块,而代理分散代码块。所以block更适用于轻便、简单的回调,如网络传输。 代理适用于公共接口较多的情况,这样做也更易于解耦代码架构。
block运行成本高。block出栈时,需要将使用的数据从栈内存拷贝到堆内存。当然如果是对象就是加计数,使用完或block置为nil后才消除。 代理只是保存了一个对象指针,直接回调,并没有额外消耗。相对C的函数指针,只是多做了一个查表动作。