KVO,NSNotification,delegete及block区别

KVO
KVO是cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监测一个值的变化,比如View的高度变化,是一对多的关系,一个值的变化会通知所有的观察者

Key-Value Observing (KVO) 建立在 KVC 之上,它能够观察一个对象的 KVC key path 值的变化
//KVO 主要用于 model , Model 类的一个属性值发生变化的时候 , 会触发事件
    // 在设计模式中 有一种设计模式是观察者模式
   
//kvo 是观察者模式的实现方式
     //kvo里面涉及的几个概念
    //1. 被观察者 一般是我们的 Model
   
//2. 观察者 一般是 controller
   
//3.key 一般是 model 类的属性
    //4.value 一般是属性对应的value

1.被观察者
@interface Person : NSObject
@property ( nonatomic , copy ) NSString *name;
@property ( nonatomic , copy ) NSString *gender;
@end


//使用步骤
//1.创建一个被观察者
_p = [[ Person alloc ] init ];
   
_p . name = @ a " ;
    _p.gender = @ b " ;

  //2. 为被观察者指定观察者
   
// 观察者 属性 key new/old/inital/Prior 参数
    [_p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

//4. 改变属性的 value
  
_p . name = @“c" ;
   
_p . name = @“d" ;
    _p . gender = @“e" ;


// whenever an observed key path changes, this method will be called
//3. 实现回调方法
- ( void )observeValueForKeyPath:( NSString *)keyPath ofObject:( id )object change:( NSDictionary *)change context:( void *)context{
        NSLog ( @"keyPath:%@" ,keyPath);
      
NSLog ( @"object:%@" ,object);
      
NSLog ( @"change:%@" ,change);
}
//5. dealloc 方法里面移除观察者
- (
void )dealloc{
    [
_p removeObserver : self forKeyPath : @"name" ];
}

KVO的实现分析
使用观察者模式需要被观察者的配合,当被观察者的状态发生变化的时候通过事先定义好的接口(协议)通知观察者.在KVO的使用中我们并不需要向被观察者添加额外的代码,就能在被观察者的属性变化的时候得到通知,这个功能是如何实现的?同KVC一样依赖于强大的Runtime机制

系统实现KVO有以下几个步骤:
  • 当类A的对象第一次被观察的时候,系统会在运行期动态创建类A的派生类。我们称为B。
  • 在派生类B中重写类A的setter方法,B类在被重写的setter方法中实现通知机制。
  • 类B重写会 class方法,将自己伪装成类A。类B还会重写dealloc方法释放资源。
  • 系统将所有指向类A对象的isa指针指向类B的对象。
KVO同KVC一样,通过isa-swizzling技术来实现.当观察者被注册为一个对象的属性的观察对象的isa指针被修改,指向一个中间类,而不是在真实的类.其结果是,isa指针的值并不一定反映实例的实际类
所以不能依靠isa指针来确定对象是否是一个类的成员,应该使用class方法来确定对象实例的类









这就是 KVO 的作用,它通过 key path 观察对象的值,当值发生变化的时候会收到通知


NSNotification
NSNotificaiion是通知,也是一对多的使用场景.在某些情况下,KVO和NSNotification是一样的,都是状态变化之后告知对方.NSNotification的特点,就是需要观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但其优点是监听不局限于属性的变化,还可以对多种多样的状态化进行jiant,监听范围广,使用也更灵活
- ( BOOL )application:( UIApplication *)application didFinishLaunchingWithOptions:( NSDictionary *)launchOptions {
   
// Override point for customization after application launch.
   
// 创建一个本地通知
   
   
UILocalNotification *localNot = [[ UILocalNotification alloc ] init ];
   
   
// 先做一下 iOS 版本的判断 , 因为 iOS8.0 以后注册发生了一些变化 , 必须设置 UIUserNotificationSettings
   
if ([[ UIDevice currentDevice ]. systemVersion floatValue ] >= 8.0 ) {
       
UIUserNotificationSettings *settings = [ UIUserNotificationSettings settingsForTypes : UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories : nil ];
       
// 注册这些 settings
        [[
UIApplication sharedApplication ] registerUserNotificationSettings :settings];
       
       
    }
   
   
// 设置本地通知的一些属性
   
// 设置通知的开始时间
   
NSDate *fireDate = [ NSDate dateWithTimeIntervalSinceNow : 10 ];
    localNot.
fireDate = fireDate;
   
   
// 设置时区
    localNot.
timeZone = [ NSTimeZone defaultTimeZone ];
   
   
// 设置重复频率 ( 现在为一分钟 )
    localNot.
repeatInterval = NSCalendarUnitMinute ;
   
   
// 推送显示的内容
    localNot.alertBody = @"12 点了 " ;
   
   
// 标题
    localNot.
alertTitle = @" 提醒 " ;
   
   
// 设置声音
    localNot.
soundName = UILocalNotificationDefaultSoundName ;
   
//localNot.soundName = @"XiaoPingGuo.aiff";
   
   
// 设置角标个数
    localNot.
applicationIconBadgeNumber = 2 ;
   
   
// 设置 userInfo
   
NSDictionary *dic = [ NSDictionary dictionaryWithObject : @"banji" forKey : @"class" ];
    localNot.
userInfo = dic;
   
   
// 执行通知
    [application
scheduleLocalNotification :localNot];
   
   
return YES ;
}

- (
void )application:( UIApplication *)application didReceiveLocalNotification:( UILocalNotification *)notification{
    [application
setApplicationIconBadgeNumber : 0 ]; // 进入前台
   
   
// 先得到所有的本地通知
   
NSArray *localNots = [application scheduledLocalNotifications ];
   
// 遍历所有的本地通知 找到对应的本地通知
   
for ( UILocalNotification *local in localNots) {
       
// 根据 userInfo 找到对应的本地通知
       
NSDictionary *dic = local. userInfo ;
       
if ([[dic objectForKey : @"class" ] isEqualToString : @"banji" ]) {
           
           
// 取消对应的本地通知
            [application
cancelLocalNotification :local];
      
        }
    }
   
// 取消全部本地通知
    [application
cancelAllLocalNotifications ];
}


Delegate
delegate是代理,就是我不想做的事情交给别人做.比如狗需要吃饭,就通过delegate通知主人,主人就会给他做饭,盛饭,倒水,这些操作这些狗都不需要关心,只需要调用delegate(代理人)就可以了,有其他类完成所需要的操作.所以delegate是一对一关系


Block
block是delegate的另一种形式,是函数式编程的一种形式,使用 场景跟delegete一样,相比delegate更灵活,而且代理的实习更直观


KVO一般的使用场景是数据,需求是数据变化,比如股票价格变化,我们一般使用KVO(观察者模式).
delegate一般的使用场景是行为,需求是需要别人帮我做一件事情,比如买卖股票,我们一般使用delegate.
Notification一般是进行全局通知,经纪人也不要知道自己的顾客.Notification是弱关联,利好消息发出,你不需要知道是谁发的也可以做出相应的反应,同理发消息的人也不需要知道接收的人也可以正常发出消息






KVC
kvc是Key Value Coding简称,是一种可以他那个过字符串的名字(key)来访问类属性的机制,而不是通过调用Setter,Getter方法访问
关键方法定义在NSKeyValueCodingProtocol
KVC支持类对象的内 建基本数据类型
使用
  • 获取值
    valueForKey: 传入NSString属性的名字。
    valueForKeyPath: 属性的路径,xx.xx
    valueForUndefinedKey 默认实现是抛出异常,可重写这个函数做错误处理

  • 修改值
    setValue:forKey:
    setValue:forKeyPath:
    setValue:forUnderfinedKey:
    setNilValueForKey: 对非类对象属性设置nil时调用,默认抛出异常。

KVC键值查找
搜索单值成员
setValue:forKey:搜索方式
1,首先搜索setKey:方法.(key指成员变量名,首字母大写)
2,上面的setter方法没找到,如果类方法axxessInstanceVariablesDirectly返回YES.那么按key,is Key,key,isKey的顺序搜索成员名. (NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES)
3,如果没有找到成员变量,调用setValue:forUnderfinedKey:

valueForKey:的搜索方式
1.首先按getKey,key,isKey的顺序查找getter方法,找到直接调用.如果是BOOL,int等内建值类型,会做NSNumber的转换
2.上面的getter没找到,查找countOfKey,objectInKeyAtIndex,KeyAtIndexes格式的方法,如果countOfKey和另外两个方法中的一个找到,那么就会返回一个可以响应NSArray所有方法的代理集合的NSArray消息方法
3.还没找到,查找countOfKey,enumeratorOfKey,memberOfKey格式的方法.如果这三个方法都找到,那么就返回一个可以响应NSSet所有方法的代理集合
4.还是没有找到,如果类方法accessInstanceVariablesDirectly返回YES,那么按key,isKey,key,isKey的顺序搜索成员名
5,再没有找到,调用valueForUndefinedKey


KVC实现分析
KVC运用了isa-swizzing技术,isa-swizzing就是类型混合指针机制,KVC通过isa-swizzing实现其内部查找定位.isa指针(is kind of)指向维护分发表的对象的类,该分发表实际上包含了指向现实类中的方法的指针和其他数据

[site setValue:@ "sitename" forKey:@ "name" ]; //会被编译器处理成
SEL sel = sel_get_uid(setValue:forKey);
IMP method = objc_msg_loopup(site->isa,sel);
method (site,sel,@ "sitename" ,@ "name" );

每一个类都由一张方法表,是一个hash表,值是还书指针IMP,SEL的名称就是查表时所有的键
SEL数据类型:查找方法表时所用的键.定义成char*,实质上可以理解成int值
IMP数据类型:他其实就是一个编译器内部实现时候的函数指针,当Objective-C编译器去处理实现一个方法的时候,就会指向一个IMP对象,这个对象是C语言表述的类型

KVC的内部机制:
一个对象在调用setValue的时候进行了:
1.根据方法名找到运行方法的时候需要的环境参数
2.他会从自己的isa指针结合环境参数,找到具体的方法实现接口
3.再直接查找得来的具体的实现方法








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值