KVO & KVC

KVO 基本使用

KVO : Key-Value Observing,俗称键值观察,可以监听到某个属性的改变.

#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong) Person *per;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    Person *p = [Person new];
    p.age = 10;
    self.per = p;
    
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.per addObserver:self forKeyPath:@"age" options:option context:nil];
    
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.per.age = 20;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"监听到%@的%@ 值改变了----%@",object,keyPath,change);
}

- (void)dealloc{
    [self.per removeObserver:self forKeyPath:@"age"];
}
@end
输出:
监听到的age 值改变了----{
    kind = 1;
    new = 20;
    old = 10;
}

KVO 本质分析

KVO & KVC_第1张图片
image.png
  • 没有使用KVO的对象的,其实例对象的isa是没有变化的(指向类对象);
  • 使用KVO的对象,其isa指向的是NSKVONotifying_person(这是个利用runtime动态创建的一个类,继承Person)

分析:当per2调用 setAge方法 的时候,本质是通过其isa找到其class对象person,在class对象中找到该方法并调用.而当per1调用setAge的时候,由于其isa指向的是NSKVONotifying_person这个类对象.会在这个类里面找到该方法并调用(这里其实调用的是Foudation框架的_NSSetIntValueAndNotify)

#import "NSKVONotifying_Person.h"

@implementation NSKVONotifying_Person

- (void)setAge:(int)age{
    
    _NSSetIntValueAndNotify();
    
}

//伪代码
void _NSSetIntValueAndNotify()
{
    
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

-(void)didChangeValueForKey:(NSString *)key{
    //通知监听器
    [self observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

@end

本质验证

KVO & KVC_第2张图片
image.png
KVO & KVC_第3张图片
image.png

小细节:

 NSLog(@"%@--%@",object_getClass(self.per1),[self.per1 class]);
 11:05:59.274011+0800 kvo[64841:2381561] NSKVONotifying_Person--Person

这里Apple重写class方法来屏蔽了NSKVONotifying_Person的存在.
补充:


- (void)printMethodNameOfClass:(Class)cls{
    unsigned int count ;
    Method *methodList = class_copyMethodList(cls, &count);
    
    NSMutableString *mStr = [NSMutableString string];
    
    for (int i = 0; i < count; i++) {
        Method method = methodList[i];
        NSString *methodName = NSStringFromSelector( method_getName(method));
        [mStr appendFormat:@"%@,",methodName];
    }
    free(methodList);
    NSLog(@"%@",mStr);
}
 11:20:07.268906+0800 kvo[65186:2398429] setAge:,class,dealloc,_isKVOA,

面试:
iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么)

  • 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类.
  • 当修改对象属性的时候,会调用Foundation_NSSetxxxValueAndNotify函数
    • willChangeValueForKey
    • 父类的setter
    • didChangeValueForKey
  • 最后内部通知监听器结果改变.

如何手动触发KVO?

  • willChangeValueForKey
  • didChangeValueForKey

直接修改成员变量会触发KVO吗?

  • 只有通过setter方法才会触发.

KVC key-Value Coding 键值编码

基本使用

      Person *p1 = [Person new];
        p1.catobjc = [cat new];
        
        [p1 setValue:@"tom" forKeyPath:@"catobjc.name"];
        [p1 setValue:@1 forKey:@"age"];
        
        NSLog(@"%@---%@",[p1 valueForKey:@"age"],[p1 valueForKeyPath:@"catobjc.name"]);

输出: 13:19:17.167290+0800 kvo[67940:2505342] 1---tom

setValue:forkey 原理:

KVO & KVC_第4张图片
image.png

valueForKey原理

KVO & KVC_第5张图片
image.png

KVC 会触发KVO 因为KVC 其实相当于调用了willChangeValueForKeydidChangeValueForKey

你可能感兴趣的:(KVO & KVC)