【iOS】—— 常用的传值方式的比较

一.常用的传值方式

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后才消除低,只是保存了一个对象指针,直接回调,并没有额外消耗
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值