小知识点好东西iOS面试库

IOS KVO原理解析与应用

2018-08-18  本文已影响2人  slimsallen
image

IOS KVO原理解析与应用

一、KVO概述

KVO,即:Key-Value Observing,是Objective-C对观察者模式的实现,每次当被观察对象的某个属性值发生改变时,注册的观察者便能获得通知,这种模式有利于两个类间的解耦合,尤其是对于业务逻辑与视图控制 这两个功能的解耦合。

二、KVO有哪些应用?

三、KVO的使用和实现?

1、使用KVO

1.注册观察者,指定被观察对象的属性:

[_people addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];

2.在观察者中实现以下回调方法:

- (void)observeValueForKeyPath:(NSString *)keyPath  
                      ofObject:(id)object  
                        change:(NSDictionary *)change  
                       context:(void *)context  
   
{  
        NSString *name = [object valueForKey:@"name"]; 
        NSLog(@"new name is: %@", name);  
}  

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

3.最后不要忘了在dealloc中移除观察者

[_people removeObserver:self forKeyPath:@"age"]; 

2、KVO的实现

KVO 在apple文档的说明

Automatic key-value observing is implemented using a technique called 
isa-swizzling… 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 …

利用运行时,生成一个对象的子类,并生成子类对象,并替换原来对象的isa指针,重写了set方法。

让我们看看代码

@interface MyProfile : NSObject
@property (nonatomic,strong) NSString *avatar;
@property (nonatomic,strong) NSString *age;
@property (nonatomic,strong) NSString *name;
@property (nonatomic,strong) NSMutableArray *dataArr;
@property (nonatomic,strong) MyDetail *myDetail;
    self.myprofile = [[MyProfile alloc]init];
    self.myprofile.name = @"sallen";
    NSLog(@"before:%s",object_getClassName(self.myprofile));
    [self.myprofile addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    self.myprofile.name = @"slim";
    NSLog(@"after:%s",object_getClassName(self.myprofile));

1.通过打印可以看出class明显发生了变化,监听之后的class替换了原有classisa指针

image

2.再看看子类

    self.myprofile = [[MyProfile alloc]init];
    self.myprofile.name = @"sallen";
    NSLog(@"before:%@",[self findSubClass:[self.myprofile class]]);
    [self.myprofile addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    self.myprofile.name = @"slim";
    NSLog(@"after:%@",[self findSubClass:[self.myprofile class]]);

通过打印可以看出明显多了个子类


image

3.对于容器的监听

 [self.myprofile addObserver:self forKeyPath:@"dataArr" options:NSKeyValueObservingOptionNew context:nil];
 [self.myprofile.dataArr addObject:@"slim"];

通过监听数组发现,是没有触发的通知的,因为重写了set方法。
我们可以利用kvc实现对数组的监听

 [[self.myprofile mutableArrayValueForKeyPath:@"dataArr"] addObject:@"slim"];
image

4.多级路径属性

Myprofile类里又包含了MyDetail
Mydetail创建了content属性
如果我们需要监听myDetail属性的变化
我们在Myprofile.m通过方法:+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
一个Key观察多个属性值的改变。

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    
    NSSet *keySet = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"myDetail"]) {
        NSSet *set = [NSSet setWithObject:@"_myDetail.content"];
        keySet = [keySet setByAddingObjectsFromSet:set];
    }
    
    return keySet;
}

打印结果:


image

四、KVO的缺陷

KVO很强大,但是也有缺点
</br>
1.只能重写 -observeValueForKeyPath:ofObject:change:contex这个方法 来获得通知,不能使用自定义的selector, 想要传一个block更是不可能 ,而且还要处理父类的情况 父类同样观察一个同样的属性的情况 ,但是有时候并不知道父类 是不是对这个消息有兴趣。
</br>
2.父类和子类同时存在KVO时,很容易出现对同一个keyPath进行两次removeObserver操作,从而导致程序crash。要避免这个问题,就需要区分出KVOself注册的,还是superClass注册的,我们可以在 -addObserver:forKeyPath:options:context:-removeObserver:forKeyPath:context这两个方法中传入不同的context进行区分。

五、block方式的实现

等待更新,
上一篇下一篇

猜你喜欢

热点阅读