KVO的坑

       KVO是一种常见的设计模式,很多IOS程序员用它来作为装逼神器,常见的应用场景是配合MVC,当Model层数据发生改变的时候,View层可以通过实现定义好的观察者模式进行数据更新。下面通过一个简单例子:小孩和保姆 来说明KVO的各种坑。

       直接上栗子:

      1、Child.h

#import <Foundation/Foundation.h>

@interface Child : NSObject

@property (nonatomic) int age;
@property (nonatomic,strong) NSString *name;

- (id)initWithName:(NSString *)name andAge:(int)age;

@end

     2、Child.m

#import "Child.h"

@implementation Child


- (id)initWithName:(NSString *)name andAge:(int)age{
    self = [super init];
    if (self) {
        self.name = name;
        self.age = age;
    }
    return self;
}

@end

    3、Nuser.h

#import <Foundation/Foundation.h>
#import "Child.h"

@interface Nurse : NSObject

@property (nonatomic,strong) Child *child;

- (id)initWithChild:(Child *)child;


@end

     4、Nuser.m

#import "Nurse.h"

@implementation Nurse

- (id)initWithChild:(Child *)child{
    self = [super init];
    if (self) {
        self.child = child;
        [self.child addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"监听名字"];
        [self.child addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:@"监听年龄"];
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"change=%@,content=%@",change,context);
    if (object == self.child && [keyPath isEqualToString:@"name"]) {
        NSLog(@"我检查到了名字");
    }else if (object == self.child && [keyPath isEqualToString:@"age"]){
        NSLog(@"我检查到了年龄");
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (void)dealloc{
    [self.child removeObserver:self forKeyPath:@"name" context:@"监听名字"];
    [self.child removeObserver:self forKeyPath:@"age" context:@"监听年龄"];
}

@end
 

     mian.m

#import <Foundation/Foundation.h>
#import "Child.h"
#import "Nurse.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Child *ch = [[Child alloc] initWithName:@"小明" andAge:12];
        Nurse *nu = [[Nurse alloc] initWithChild:ch];

        //改变名字
        [ch setName:@"我改名字了"];
        
        //改变年龄
        [ch setAge:100];
        
        //KVC也能触发KVO
        [ch setValue:@"KVC" forKey:@"name"];
        

        
    }
    
    
    return 0;
}



      这个栗子保姆通过观察孩子的姓名和年龄,分别打印不同的内容。保姆是观察者,小孩是被观察者。Chiled的两个属性:name 和 age是观察的key。

      坑1:因为保姆注册了对小孩的2个不同的观察者:name和age,所以在

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
方法中,要针对不同的object 和 keyPath进行不同处理,否则会漏掉某个key的观察

      坑2:我们假设小孩类还有父类,并且父类也绑定了一些其他KVO,我们看到,上述回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的父类或父类的父类...因此在上述回调函数里面还要加这么一句:[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];

     坑3:相信大家都用过MJRefresh下拉刷新的控件吧,我记得当时开始用这个东西的时候经常crash掉,提示这样的信息,相信大家很熟悉这个错误:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x100204980 of class Child was deallocated while key value observers were still registered with it.

    当对象已经被release,如果此对象的观察者还没有移除的时候程序就crash了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值