iOS中通知、代理、Block、KVO
在一个复杂的,有状态的系统中,当一个对象的状态发生改变,如何通知系统,并对状态改变做出相应的行为是必需考虑的一个问题,在iOS中为这类问题提供了4种解决方法:
1、NSNotifiactaion和NSNotificationCenter:通知中心
2、Delegate:代理
3、Block:回调(Callback)
4、KVO(Key-Value Observing):键值观察
一、NSNotification 和 NSNotificationCenter
每个运行中的application都有一个NSNotificationCenter的成员变量,它的功能就类似公共栏。对象注册关注某个确定的notification,我们把这些注册对象叫做 observer。其它的一些对象会给center发送notifications,center将该notifications转发给所有注册对该notification感兴趣的对象,我们把这些发送notification的对象叫做 poster。
1、在要发出通知消息的地方:
[[NSNotificationCenter defaultCenter] postNotificationName:@"Notification_isHaveBanner" object:nil userInfo:@{@"ishavebanner":@"1"}];
2、在要接收通知消息的地方:
//对象注册,并关连消息
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reciveBannernNotice:) name:@"Notification_isHaveBanner" object:nil];
#pragma mark 接收通知处理传递消息
-(void) reciveBannernNotice:(NSNotification *)notification
{
NSString *bannerStr = [notification.userInfo objectForKey:@"ishavebanner"];
if ([bannerStr isEqualToString:@"0"]) {
isHaveBanner = NO;
}else if([bannerStr isEqualToString:@"1"])
{
isHaveBanner = YES;
}else{
isHaveBanner = NO;
}
}
一、Delegate(代理)
delegate(代理)是iOS编程的一种设计模式,简单来说就是一个委托方的类让另外一个代理方的类具体实现其定义的方法。怎样写一个代理设计模式
1、你要明确你的协议名称,一般来讲名称都是:控件类名 + Delegate
2、代理方法中一般都是声明为@optional(程序默认情况下是@required)
3、代理方法名一般以控件开头
4、代理方法至少有一个参数
举例:来个保姆和婴儿之间是怎样利用代理协议来实现一个简单的找过过程,婴儿类要坐的事委托保姆类做。
1、首先,我们创建一个婴儿类,继承自NSObject ,接下来在Baby.h文件中创建下面的代码
#import <Foundation/Foundation.h>
// 定义一份代理协议
@protocol BabyDelegate <NSObject>
- (void)babyWantEat:(Baby *)baby;
- (void)babyWantSleep:(Baby *)baby;
@end
@interface Baby : NSObject
/** 吃了多少东西 */
@property (nonatomic, assign) int food;
/** 睡意 */
@property (nonatomic, assign) int sleep;
/** 饿了 */
- (void)wantEat;
/** 困了 */
- (void)wantSleep;
/** 代理对象 */
@property (nonatomic, weak) id<BabyDelegate> delegate;
@end
2、接下来是Baby.m文件中创建下面的文件
#import "Baby.h"
@implementation Baby
- (void)wantEat
{
NSLog(@"婴儿想吃东西");
// 通知保姆喂婴儿
if ([self.deleate respondsToSelector:@selector(babyWantEat:)]){
[self.delegate babyWantEat:self];
}
}
- (void)wantSleep
{
NSLog(@"婴儿想睡觉");
// 通知保姆哄婴儿睡觉
if ([self.deleate respondsToSelector:@selector(babyWantSleep:)]){
[self.delegate babyWantSleep:self];
}
}
@end
3、下面创建一个保姆类同样继承自NSObject,下面是Nurse.h文件中的代码
#import "Baby.h"
#import <Foundation/Foundation.h>
@interface Nurse : NSObject <BabyDelegate>
@end
4、接下来是Nurse.m文件中保姆需要在代理委托方法中做她的工作
#import "Nurse.h"
@implementation Nurse
- (void)babyWantEat:(Baby *)baby
{
//baby是代理委托方传过来的值
baby.food += 20;
NSLog(@"Nurse喂婴儿吃东西--现在的食量是%d", baby.food);
}
- (void)babyWantSleep:(Baby *)baby
{
baby.sleep += 20;
NSLog(@"Nurse哄婴儿睡觉--现在的睡意是%d", baby.sleep);
}
@end
三、Block(回调)
直接举例:一个Block回调修改上一界面的背景颜色。
ViewController1 控制器1,ViewController2 控制器2。控制器1跳转到控制器2,然后在控制器2触发事件回调修改控制器1的背景颜色为红色。
1、ViewController2实现:
#import <UIKit/UIKit.h>
@interface ViewController2 : UIViewController
/**
* 定义了一个changeColor的Block。这个changeColor必须带一个参数,这个参数的类型必须为id类型的
* 无返回值
* @param id
*/
typedef void(^changeColor)(id);
/**
* 用上面定义的changeColor声明一个Block,声明的这个Block必须遵守声明的要求。
*/
@property (nonatomic, copy) changeColor backgroundColor;
@end
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//声明一个颜色
UIColor *color = [UIColor redColor];
//用刚刚声明的那个Block去回调修改上一界面的背景色
self.backgroundColor(color);
}
1、ViewController1实现:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
ViewController2 *vc =[[ViewController2 alloc]init];
// 回调修改颜色
vc.backgroundColor = ^(UIColor *color){
self.view.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];
}
四、KVO(Key-Value Observing)
KVO是Object-C中定义的一个通知机制,其定义了一种对象间监控对方状态的改变,并做出反应的机制。对象可以为自己的属性注册观察者,当 这个属性的值发生了改变,系统会对这些注册的观察者做出通知。其用途十分广泛,比方说,你的下载进度条是根据下载百分比决定的,那么,可以通过观察下载百 分比的改变,刷新进度条的样式,来直观的反应下载进度等等。
A.注册观察者:
//第一个参数observer:观察者 (这里观察self.myKVO对象的属性变化)
//第二个参数keyPath: 被观察的属性名称(这里观察self.myKVO中num属性值的改变)
//第三个参数options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
//第四个参数context: 上下文,可以为kvo的回调方法传值(例如设定为一个放置数据的字典)
[self.myKVO addObserver:self forKeyPath:@"num" options:
NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
注意:这里的self.myKVO是被观察的对象,可以是self本身、也可以是定义的self.tableview等等。forKeyPath对应的一定要是这个对象的属性名称。
B. 属性(keyPath)的值发生变化时,收到通知,调用以下方法:
//keyPath:属性名称
//object:被观察的对象
//change:变化前后的值都存储在change字典中
//context:注册观察者时,context传过来的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
//do something...
}
其他相关链接:开发该选择Blocks还是Delegates