细说KVO

1.手动控制kvo的触发
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key; 返回NO ;

然后在改变value前 调用[object willChangeValueForKey:@"key"];
在改变value后, 调用[object didChangeValueForKey:@"key"] ;

2.隔层kvo观察

@interface Dog : NSObject

@property(nonatomic)NSInteger age;

@property(nonatomic)NSInteger *level;

@end

#import <Foundation/Foundation.h>

#import "Dog.h"

@interface Person : NSObject

@property(nonatomic,strong)Dog *dog;

@end

#import "Person.h"

@implementation Person

-(instancetype)init{

    self = [super init];

    if (self) {

        _dog = [[Dog alloc]init];

    }

    return self;

}

+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{

    NSSet *keyPath = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"dog"]) {

        NSArray *arr = @[@"_dog.level",@"_dog.age"];

        keyPath = [keyPath setByAddingObjectsFromArray:arr];

    }

    return keyPath;

}

@end

//设置观察

  self.person = [[Person alloc] init];

 [self.person addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionOld context:nil];

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context

{

    NSLog(@"change:%@",change);

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event  {

    self.person.dog.age++;

}


2.KVO的底层实现

#import "NSObject+PRKVO.h"

#import <objc/runtime.h>

@implementation NSObject (PRKVO)

-(void)PR_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context

{

    //1.自定义一个NSKVONotifying_Person子类

    NSString *oldClassName = NSStringFromClass(self.class);

    NSString *newClassName = [@"PRKVO_" stringByAppendingString:oldClassName];

    //创建一个类

    Class MyClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0);

    //注册该类

    objc_registerProtocol(MyClass);

    

    //2.动态修改

    object_setClass(self, MyClass);

    

    //3.添加setName方法,重新父类setName

    class_addMethod(MyClass, @selector(setName:), (IMP)haha, "v@:@");

    

}

void haha(id self,SEL _cmd,NSString *newName){

    NSLog(@"!%@",newName);

}

 



原文地址:https://www.cnblogs.com/PJXWang/p/8431666.html