IOS KVO 机制

一 KVO简介

     KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。这样开发人员不需要自己去实现这样的方案:每次属性改变了就发送消息通知,NSObject实现了这个协议从而使KVO获得框架级支持,可以方便地采用, 这是KVO最大优点,
      KVO并不是什么新鲜事物,它来源于设计模式中的 观察者模式 ,其基本思想就是: 一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。这个主动通知通常是通过调用各观察者对象所提供的接口方法来实现的。观察者模式较完美地将目标对象与观察者对象解耦。 

二 使用方法

先看一段代码:首先定义Student类

Student.h文件
#import <Foundation/Foundation.h>
@interface Student : NSObject
{
   
 NSString *name;
   
 NSString *courseName;
}
@property (nonatomic,strong) NSString *courseName;
-( void )changeCourseName:( NSString *) newCourseName;
@end

Student.m文件
@implementation Student
@synthesize  courseName =  _courseName ;
-(
void)changeCourseName:(NSString*) newCourseName
{
   
 courseName = newCourseName;
}
@end

然后在UIViewController类中有一个Student对象成员

@interface WBViewController()
@property(nonatomic,strong) Student *stu;
@end

@implementation WBViewController
@synthesize stu = _stu;


/**************** 2.观察者实现回调方法*****************/
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
   
 if ([keyPath isEqual:@"courseName"])
    {
        
NSLog(@"PageView课程被改变了  %s",(char*)context);
  //输出被观察者携带的参数
         NSLog ( @"PageView 新课程是 :%@  老课程是 :%@" , [change objectForKey: @"new" ],[change objectForKey: @"old" ]);
    }
     else
    {
        [
super observeValueForKeyPath:keyPath
                            
 ofObject:object
                              
 change:change
                             
 context:context];
    }
//注意:在实现处理变更通知方法 observeValueForKeyPath 时,要将不能处理的 key转发给super observeValueForKeyPath 来处理。
}
-(
void ) viewWillAppear:( BOOL )animated
{
    
char   *param =   "I am a student!" ;  
   
  self . stu   = [[ Student   alloc ]   init ];
     /***********1.注册,指定被观察者的属性**************/
    [
self.stu addObserver:self
          
 forKeyPath:@"courseName"
             
 options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew
             
 context:param];        //携带参数

    [
self.stu changeCourseName:@"数学课"];
   
 NSLog(@"初始值:%@", [self.stu valueForKey:@"courseName"]);
   
}

-(
void) viewDidAppear:(BOOL)animated
{
    /**************3. 利用 KVC 机制改变被观察者属性值 ****************/
    
 // [ self . stu  setValue : @" 化学课 "  forKey: @"courseName" ];
     [self.stu setCourseName:@"化学课"];
}
   /**************4.使用完后移除观察者****************/
-(void) dealloc
{
    [
self.stu removeObserver:self forKeyPath:@"courseName" context:nil];
}



程序输出结果
2013-10-24 21:44:51.810 KVODemo[7135:c07] 初始值:数学课
2013-10-24 21:44:51.828 KVODemo[7135:c07] PageView
课程被改变了  I am from a student!
2013-10-24 21:44:51.828 KVODemo[7135:c07] PageView新课程是:化学课 老课程是:数学课
     
 程序解释:
       在-(void) viewWillAppear:(BOOL)animate 中创建Student对象,并给courceName赋初值,然后给对象stu添加观察者,当courseName属性值发生变化的时候调用观察者的-(voidobserveValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context方法,并且携带了参数。
    在-(void) viewDidAppear:(BOOL)animated中我们使用KVC机制改变了courseName属性的值,此时通过输出结果发现调用了观者者的方法。

     注意:
        1.KVO使用步骤 a.注册观察者  b.观察者实现回调函数 
                     c.被观察者的观察属性通过KVC机制被修改    
                     d.使用完后移除观察者
        2.[student changeCourseName:@"英语课"]; 直接改变courseName属性值的时候并不能触发观察者的回调方法,只有通过KVC机制的时候才可以触发, 也就是说要遵循使用属性的 setter 方法
        3.上面的代码我们采用的是KVO消息的默认通知机制,即当被观察对象的相应的属性发生变化的时候,系统会自动的通知观察者,除此之外我们也可以使用方法 willChangeValueForKey:和didChangeValueForKey:来实现手动的通知修改后的部分代码:
首先我们在Student类中添加类方法:
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key
{
   
 if ([key isEqualToString:@"courseName"]) {
       
 return NO;
    }
   
   
 return [super automaticallyNotifiesObserversForKey:key];
}

  该方法表示收到属性变量名为courseName的值改变时候不向观察者发送消息,此时我们如果运行程序的话在观察者的回调函数中接受不到消息,说明程序已经无法自动的来发送消息了。此时我们只能手动的来向观察者发送消息,修改之前的例子代码:
-(void) viewDidAppear:(BOOL)animated
{
   
 /**************3.利用KVC机制改变被观察者属性值****************/
   
 //[self.stu setValue:@"化学课" forKey:@"courseName"];
    [
self.stu willChangeValueForKey:@"courseName"];
    [
self.stu setCourseName:@"化学课"];
    [
self.stu didChangeValueForKey:@"courseName"];
}

我们在[self.stu setCourseName:@"化学课"];函数调用前后,分别调用willChangeValueForKey:和didChangeValueForKey:从而实现了手动显示的向观察者发送消息。
      
      4.除此之外我们还可以KVO的依赖键,即一个属性的值依赖于另一对象中的一个或多个属性,如果这些属性中任一属性的值发生变更,被依赖的属性值也应当为其变更进行标记。在此就不在赘述,如果感兴趣可以去参看苹果的KVO官方文档:

三 实现的原理
  
        键值观察用处很多,Core Binding 背后的实现就有它的身影,那键值观察背后的实现又如何呢?想一想在上面的自动实现方式中,我们并不需要在被观察对象 Target 中添加额外的代码,就能获得键值观察的功能,这很好很强大,这是怎么做到的呢?答案就是 Objective C 强大的 runtime 动态能力,下面我们一起来窥探下其内部实现过程。
      当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类(中间类),在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。

官方文档部分说明:

Key-Value Observing Implementation Details

Automatic key-value observing is implemented using a technique called isa-swizzling.

The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.

When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.

You should never rely on the  isa  pointer to determine class membership. Instead, you should use the  class  method to determine the class of an object instance.

四 总结

1. KVO的架构非常的强大,可以很容易的支持多个观察者观察同 一个属性,以及相关的值。
2.  KVO 理论上是对象之间的联系而非类之间, 你必须要有实体 并改变实体的属性 ,那么关注这个属性改变的其他实体就可以接到通知。
3.KVO这种编码方式使用起来很简单,很适用于datamodel修改后,引发的UIVIew的变化这种情况。例如:当一个UITextField ”关注“的一个对象的字符串被修改的时候,同时通知UITexTFiled更新自己的显示内容。即自动实现“文档与视图的”的绑定。
4.KVO机制和NSNotification有点像,区别在于KVO关注的是对象的某个属性,KVO利用KVC机制来触发消息给观察者,而且只能触发观察者实现的-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context方法,而NSNotification关注的是对象本身的行为,需要对象自己来postNotification,它可以调用观察者注册时的一个方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter和Objective-C(简称OC)都是开发移动应用程序的技术,但它们在很多方面有着不同的特点。 首先,Flutter是一种跨平台的移动应用开发框架,由谷歌开发。它使用Dart语言编写,具有热重载、响应式UI框架和丰富的UI组件等特点。Flutter的一大优势是可以同时在iOS和Android平台上开发应用程序,并且拥有高性能和良好的用户体验。Flutter也支持使用原生代码进行集成,因此可以很好地与Objective-C进行交互。 Objective-C是一种面向对象的编程语言,主要用于iOS和macOS平台的应用程序开发。Objective-C采用了一种称为KVO(Key-Value Observing)的机制,允许对象对属性和值的变化进行观察和响应。通过注册观察者,当被观察对象的属性发生变化时,观察者可以接收到通知并执行相应的操作。KVO是一种非常强大的工具,可以用于实现对象之间的数据绑定和通信。 在使用Flutter开发应用时,可以与Objective-C进行集成,并利用Objective-C提供的KVO机制来实现对Flutter应用内部变量的监视和响应。这可以通过在Flutter与Objective-C之间建立桥接来实现,从而达到在Flutter应用使用KVO的目的。 总的来说,Flutter和Objective-C KVO是两种不同的技术,Flutter是一个跨平台的移动应用开发框架,而Objective-C KVO是一种可以用于观察和响应对象属性变化的机制。在合适的场景下,可以通过Flutter与Objective-C进行集成,从而利用KVO机制来实现对Flutter应用内部变量的监视和响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值