KVO,NSNotification,delegete及block区别

来源:互联网 发布:html5简单小游戏源码 编辑:程序博客网 时间:2024/05/17 08:19
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.被观察者
@interfacePerson :NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *gender;
@end


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

 //2.为被观察者指定观察者
   
//观察者属性key new/old/inital/Prior参数
    [_paddObserver:selfforKeyPath:@"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{
    [
_premoveObserver:selfforKeyPath:@"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 = [[UILocalNotificationalloc]init];
   
   
//先做一下iOS版本的判断,因为iOS8.0以后注册发生了一些变化,必须设置UIUserNotificationSettings
   
if ([[UIDevicecurrentDevice].systemVersionfloatValue] >=8.0) {
       
UIUserNotificationSettings *settings = [UIUserNotificationSettingssettingsForTypes:UIUserNotificationTypeAlert| UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
       
//注册这些settings
        [[
UIApplicationsharedApplication]registerUserNotificationSettings:settings];
       
       
    }
   
   
//设置本地通知的一些属性
   
//设置通知的开始时间
   
NSDate *fireDate = [NSDatedateWithTimeIntervalSinceNow:10];
    localNot.
fireDate= fireDate;
   
   
//设置时区
    localNot.
timeZone= [NSTimeZonedefaultTimeZone];
   
   
//设置重复频率(现在为一分钟)
    localNot.
repeatInterval= NSCalendarUnitMinute;
   
   
//推送显示的内容
    localNot.alertBody= @"12点了";
   
   
//标题
    localNot.
alertTitle= @"提醒";
   
   
//设置声音
    localNot.
soundName= UILocalNotificationDefaultSoundName;
   
//localNot.soundName = @"XiaoPingGuo.aiff";
   
   
//设置角标个数
    localNot.
applicationIconBadgeNumber= 2;
   
   
//设置userInfo
   
NSDictionary *dic = [NSDictionarydictionaryWithObject:@"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*localin localNots) {
       
//根据userInfo找到对应的本地通知
       
NSDictionary *dic = local.userInfo;
       
if ([[dicobjectForKey:@"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"]; //会被编译器处理成
SELsel = sel_get_uid(setValue:forKey);
IMPmethod= 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
原创粉丝点击