一.常用的传值方式
1.KVO传值
KVO全称Key Value Observing,是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。由于KVO的实现机制,只针对属性才会发生作用,一般继承自NSObject的对象都默认支持KVO。
KVO可以监听单个属性的变化,也可以监听集合对象的变化。通过KVC的mutableArrayValueForKey:等方法获得代理对象,当代理对象的内部对象发生改变时,会回调KVO监听的方法。集合对象包含NSArray和NSSet。
KVO传值的步骤:
- 步骤一:通过addObserver:forKeyPath:options:context:方法注册观察者,观察者可以接收keyPath属性的变化事件
/*
@observer:就是观察者,是谁想要观测对象的值的改变。
@keyPath:就是想要观察的对象属性。
@options:options一般选择NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,这样当属性值发生改变时我们可以同时获得旧值和新值,如果我们只填NSKeyValueObservingOptionNew则属性发生改变时只会获得新值。
@context:想要携带的其他信息,比如一个字符串或者字典什么的,不过只能是同一视图的内容。
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- 步骤二:在观察者中实现observeValueForKeyPath:ofObject:change:context:方法,当keyPath属性发生改变后,KVO会回调这个方法来通知观察者
/*
@keyPath:观察的属性
@object:观察的是哪个对象的属性
@change:这是一个字典类型的值,通过键值对显示新的属性值和旧的属性值
@context:上面添加观察者时携带的信息
*/
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
- 步骤三:当观察者不需要监听时,可以调用removeObserver:forKeyPath:方法将KVO移除
/*
@observer:就是观察者,是谁想要观测对象的值的改变。
@keyPath:就是想要观察的对象属性。
*/
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
注:
- KVO只是监听setter方法,例如像可变数组添加元素的方法(addObject)它不属于setter方法,所以即使你向数组中add多少个元素也不会有监听反应。
- 在不使用时一定要移除KVO。
详细的介绍可以看:【iOS】—— KVO传值
2.通知传值
通知中心传值,可以跨越多个页面传值, 一般也是从后面的页面传给前面的页面。
通知传值的步骤:
- 步骤一:在发送者(视图二)中实现一个方法进行发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"TransDataNoti" object:nil userInfo:_dictionary];
//postNotificationName:之后的参数就是这个通知的名字,要和要和接收者中的名字一样,才能让接收者正确接收。
//object:接收对象。
//userInfo: 携带的参数,为字典类型的数据,在例子中我携带了一个字典,因为有时候我们要传递的参数不只是一个,所以把东西全部放在通知里面,在接收者中,根据字典里面的键来取出里面的值。
//_dictionary是我之前定义的一个字典实例。
- 步骤二:在接收者(视图一)中注册通知,也就是接收者要进行接收通知,接收通知和发送通知的名字要一致
//注册通知,用于接收通知,接收通知的名称必须和发送通知的名称保持一致才能接收到,否则无法接收到发出的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notiReceived:) name:@"TransDataNoti" object:nil];
//注:务必让接收者中name后面的参数和发送者中的name后面的参数一样。
- 步骤三:在接收者(视图一)中实现通知中的方法
// 参数类型是NSNotification
- (void)notiReceived:(NSNotification*)sender {
self.textField.text = sender.userInfo[@"content"];
self.label.text = sender.userInfo[@"content"];
}
注:- iOS9以上对其不进行移除处理不会崩溃,但是如果这个对象是在监听一个单例对象的属性的话,在其他地方,又修改该属性,还是会崩溃。
- (void)dealloc {
//移除所有通知
// [[NSNotificationCenter defaultCenter] removeObserver:self];
//移除某个通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"TransDataNoti" object:nil];
}
参数Observer为要删除的观察者,一定不能置为nil。
如果为nil它会报警告:
Null passed to a callee that requires a non-null argument。
详细的介绍可以看:【iOS】—— 通知传值
3.代理传值
通过协议和代理来实现的一种传值方式。
协议传值的步骤:
- 步骤一:在视图B中声明一份协议
//定义一个代理协议,B视图控制器的协议
@protocol VCSecondDelegate <NSObject>
//定义一个协议函数,改变A视图的背景颜色
- (void) changeColor:(UIColor*) color;
@end
- 步骤二:在B视图中声明一个代理属性
//定义一个代理对象
//这个代理对象会执行协议函数
//通过代理对象来实现协议函数,达到代理对象改变本身属性的目的
//代理对象一定要实现代理协议
@property (assign, nonatomic) id<VCSecondDelegate> delegate;
- 步骤三:在跳转回视图A的方法里调用代理执行的方法
//点击按钮时,触发按钮的事件
- (void) pressChange {
//代理对象调用事件函数
[_delegate changeColor:[UIColor purpleColor]];
}
- 步骤四:在视图A里签订代理协议
@interface VCfirst : UIViewController<VCSecondDelegate>
- 步骤五:签订代理人
//点击屏幕空白处推出视图控制器B
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
VCsecond* vcSecond = [[VCsecond alloc] init];
vcSecond.title = @"视图B";
//将当前的控制器作为代理对象赋值
vcSecond.delegate = self;
//推出视图控制器B
[self.navigationController pushViewController:vcSecond animated:YES];
}
- 步骤六:实现代理方法
//通过代理的协议来改变背景颜色
- (void) changeColor:(UIColor *)color {
self.view.backgroundColor = color;
}
详细的介绍可以看:【iOS】—— 协议传值和属性传值
4.block传值
将函数及其执行上下文封装起来的对象,block的调用实际就是函数的调用。
常用于回调,简单的说就是视图B有一个按钮,当按钮被点击时把点击事件传给视图A,并传一个字符串"B被点了"或其它你想要传递的内容。
block传值的步骤:
- 步骤一:block 的声明 (视图二 .h 中)
typedef 返回值类型(^block 的名字)(参数列表);
//自定义类型Block
typedef void(^returnLabelValue)(NSString *label);
//Block声明
@property (nonatomic, strong) returnLabelValue valueLabel;
- 步骤二:block 的调用 (视图二 .m 中)
block的名字(想要传递的数据);
注:想要传递的数据必须和上边声明block时的类型相同
//Block传值
self.valueLabel(self.textField.text);
- 步骤三:block 实现(视图一的创建视图二时)
block的名字 = ^(参数列表) { 使用调用的参数列表 };
change.valueLabel = ^(NSString *text) {
self.label.text = text;
};
//change是创建的视图二的名字
详细的介绍可以看:【iOS】—— Block传值
二.常用的传值方式的比较
通知和代理的比较
通知 | 代理 | |
---|---|---|
功能 | 对象之间的信息传递 | 对象之间的信息传递 |
效率 | 低 | 高 |
关联 | 弱关联,不需要知道是谁发,也不需要知道是谁接收 | 强关联,委托和代理双方互相知道 |
传递方式 | 一对多,全局通知,跨域通信更广 | 一对一,一般是行为需要别人来完成 |
KVO和通知的比较
KVO | 通知 | |
---|---|---|
功能 | 对象之间的信息传递 | 对象之间的信息传递 |
监听范围 | 监听一个值的变化,通知不局限于监听属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用更灵活 | 全局通知 |
传递关系 | 一对多 | 一对多 |
使用场景 | 监听数据变化 | 全局通知 |
block和代理的比较
block | 代理 | |
---|---|---|
功能 | 对象之间的信息传递 | 对象之间的信息传递 |
使用场景 | 回调 | 回调 |
代码块 | 集中,更适用于轻便,简单的回调,例如网络传输 | 分散,适用于公共接口较多的情况 |
运行成本 | 高,block出栈时,需要将使用的数据从栈内存拷贝到堆内存。当然如果是对象就是加计数,使用完成block置为nil后才消除 | 低,只是保存了一个对象指针,直接回调,并没有额外消耗 |