美文网首页
iOS编程观察者模式的底层实现

iOS编程观察者模式的底层实现

作者: 单线程Jack | 来源:发表于2019-07-22 09:08 被阅读0次

    KVO(Key Value Observing, 键值观察)是一种设计模式,是Objective-C对观察者模式的实现,每次当被观察对象的某个属性值发生改变时,注册的观察者便能获得通知。

    KVO实现的三个步骤:

    <1>注册观察者,指定被观察对象的属性:
     [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    //通知其他对象的方法,这个方法在NSObject中就已经申明了,也就是说任何继承自NSObject的对象都可以使用KVO.
    
    <2>在观察者中实现以下回调方法:
    - (void)observeValueForKeyPath:(NSString *)keyPath  
         ofObject:(id)object  
                        change:(NSDictionary *)change  
                       context:(voidvoid *)context  
         {  
            // use the context to make sure this is a change in the address,  
            // because we may also be observing other things 
     
            NSString *name = [object valueForKey:@"name"]; 
            NSLog(@"new name is: %@", name);  
        } 
    

    当person对象中的name属性发生变化,系统会自动调用该方法。

    <3>在dealloc中移除观察者
    -(void)dealloc  
    {  
          // must stop observing everything before this object is  
          // deallocated, otherwise it will cause crashes  
          for(Person *p in m_observedPeople){  
              [p removeObserver:self forKeyPath:@"name"];  
          }  
     
          [m_observedPeople release];  
          m_observedPeople = nil;  
      }  
    

    添加的观察者的次数要和移除观察者的次数相等,少移除一个或者多移除一个都会造成程序崩溃。

    1、KVO的实现原理:

    当设置一个类为观察对象时,系统会动态地创建一个新的类,这个新的类继承自被观察对象的类,还重写了基类被观察属性的setter方法。派生类在被重写的setter方法中实现真正的通知机制。最后,系统将这个对象的isa指针指向这个新创建的派生类,这样,被观察对象就变成了新创建的派生类的实例。(注:runtime中,对象的isa指针指向该对象所属的类,类的isa指针指向该类的metaclass。有关OC的对象、类对象、元类对象metaclass object和isa指针,请戳这里详细了解)。同时,新的派生类还重写了dealloc方法(removeObserver)。

    2、KVO的缺陷

    不可否认,KVO的功能确实很强大,但是它的缺点也很明显:

    ①过于简单的API

    KVO中只有通过重写-observeValueForKeyPath:ofObject:change:context方法来获取通知,该方法有诸多限制:不能使用自定义的selector,不能使用block,而且当父类也要监听对象时,往往要写一大坨代码。

    ②父类和子类同时存在KVO时(监听同一个对象的同一个属性),很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash。要避免这个问题,就需要区分出KVO是self注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:和-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。

    相关文章

      网友评论

          本文标题:iOS编程观察者模式的底层实现

          本文链接:https://www.haomeiwen.com/subject/btqmlctx.html