KVO

KVO基本使用

KVO原理分析

#import "TZSecondViewController.h"
#import "TZPerson.h"
#import &ltobjc/runtime.h>

@interface TZSecondViewController ()
@property (nonatomic, strong) TZPerson* p;
@end

@implementation TZSecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _p = [TZPerson new];
    Class class = NSClassFromString(@"NSKVONotifying_TZPerson");
    if (class) {
        NSLog(@"class exist");
    } else {
        NSLog(@"class not exist");
    }
    // 添加观察者
    [_p addObserver:self forKeyPath:@"steps" options:NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew context:nil];
    
    //添加观察者之后动态生成的类:NSKVONotifying_TZPerson
    Class class1 = NSClassFromString(@"NSKVONotifying_TZPerson");
    if (class1) {
        NSLog(@"class1 exist");
    } else {
        NSLog(@"class1 not exist");
    }
    
    [self printMethods:class1];
    [self printClasses:[TZPerson class]];
    }

/// 打印对应的类及子类
- (void) printClasses:(Class) cls {
    /// 注册类的总数
    int count = objc_getClassList(NULL, 0);
    /// 创建一个数组, 其中包含给定对象
    NSMutableArray* array = [NSMutableArray arrayWithObject:cls];
    /// 获取所有已注册的类
    Class* classes = (Class*)malloc(sizeof(Class)*count);
    objc_getClassList(classes, count);
    /// 遍历s
    for (int i = 0; i < count; i++) {
        if (cls == class_getSuperclass(classes[i])) {
            [array addObject:classes[i]];
        }
    }
    free(classes);
    NSLog(@"classes = %@", array);
    }

- (void) printMethods:(Class)cls {
    unsigned int count = 0;
    Method* methods = class_copyMethodList(cls, &count);
    NSMutableArray* array = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        Method method = methods[i];
        SEL sel = method_getName(method);
        IMP imp = method_getImplementation(method);
        NSString* methodName = NSStringFromSelector(sel);
        [array addObject:methodName];
    }
    NSLog(@"%@", array);
    free(methods);
    }

// 实现监听方法
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@", change);
    }
    /**
     lldb下断点跟:watchpoint set variable self->_p->_steps
     大概顺序:改变父类的值,通知观察者发生改变
     NSKeyValueWillChange
     [TZPerson setSteps:]
     NSKeyValueDidChange
     NSKeyValueNotifyObserver
 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    _p.steps++;// setter getter
    }

/*
 - (Class) class {
 //重写class把父类返回
  return object_superClass(object_getClass(self));
  }
  */
- (void) dealloc {
    [_p removeObserver:self forKeyPath:@"steps"];
    NSLog(@"");
    }
    @end

KVO自定义

KVO延伸

自动销毁机制

Blocks

#import "NSObject+KVO.h"
#import &ltobjc/message.h>

static const char* kTZKVOAssiociateKey = "kTZKVOAssiociateKey";

@interface TZInfo : NSObject
@property (nonatomic, weak) NSObject* observer;
@property (nonatomic, strong) NSString* keyPath;
@property (nonatomic, copy) TZKVOBlock hanleBlock;
@end


@implementation TZInfo

- (instancetype) initWithObserver:(NSObject*)observer forKeyPath:(NSString*) keyPath handleBlock:(TZKVOBlock) block {
    if (self == [super init]) {
        _observer = observer;
        _keyPath = keyPath;
        _hanleBlock = block;
    }
    return self;
    }
    @end


@implementation NSObject (KVO)

- (void) tz_addObserverBlock:(NSObject*) observer forKeyPath:(NSString*) keyPath handle:(TZKVOBlock) handleBlock {
  
    // 动态创建一个子类
    Class newClass = [self createClass:keyPath];
    
    // 修改了isa的指向
    object_setClass(self, newClass);
    
    // 信息保存
    TZInfo* info = [[TZInfo alloc] initWithObserver:observer forKeyPath:keyPath handleBlock:handleBlock];
    NSMutableArray* array = objc_getAssociatedObject(self, kTZKVOAssiociateKey);
    if (!array) {
        array = [NSMutableArray array];
        objc_setAssociatedObject(self, kTZKVOAssiociateKey, array, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    [array addObject:info];
  }

/// 添加观察者
- (void)gv_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
  
    // 动态创建一个子类
    Class newClass = [self createClass:keyPath];
    
    // 修改了isa的指向
    object_setClass(self, newClass);
    
    // 关联方法
    objc_setAssociatedObject(self, (__bridge void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
  }

// NSKVONotifying_TZPerson
- (Class) createClass:(NSString*) keyPath {
  
    // 1. 拼接子类名 // Person
    NSString* oldName = NSStringFromClass([self class]);
    NSString* newName = [NSString stringWithFormat:@"TZKVONotifying_%@", oldName];
    
    // 2. 创建并注册类
    Class newClass = NSClassFromString(newName);
    if (!newClass) {
        
        // 创建并注册类
        newClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
        objc_registerClassPair(newClass);
        
        // 添加一些方法
        // class
        Method classMethod = class_getInstanceMethod([self class], @selector(class));
        const char* classTypes = method_getTypeEncoding(classMethod);
        class_addMethod(newClass, @selector(class), (IMP)tz_class, classTypes);
        
        // setter
        NSString* setterMethodName = setterForGetter(keyPath);
        SEL setterSEL = NSSelectorFromString(setterMethodName);
        Method setterMethod = class_getInstanceMethod([self class], setterSEL);
        const char* setterTypes = method_getTypeEncoding(setterMethod);
        
        class_addMethod(newClass, setterSEL, (IMP)tz_setter, setterTypes);
        
        // 添加析构方法
        SEL deallocSEL = NSSelectorFromString(@"dealloc");
        Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);
        const char* deallocTypes = method_getTypeEncoding(deallocMethod);
        class_addMethod(newClass, deallocSEL, (IMP)myDealloc, deallocTypes);

    }
    return newClass;
  }

void myDealloc(id self, SEL _cmd) {
    // 父类
    Class superClass = [self class];//class_getSuperclass(object_getClass(self));
    
    object_setClass(self, superClass);
    
    NSLog(@"");
}

- (void) hookDealloc {
    Method m1 = class_getInstanceMethod(object_getClass(self), NSSelectorFromString(@"dealloc"));
    Method m2 = class_getInstanceMethod(object_getClass(self), @selector(myDealloc));
    method_exchangeImplementations(m1, m2);
    }

#pragma mark - c 函数
static void tz_setter(id self, SEL _cmd, id newValue) {
    NSLog(@"%s", __func__);
    
    struct objc_super superStruct = {
        self,
        class_getSuperclass(object_getClass(self))
    };
    
    // keypath
       NSString* keyPath = getterForSetter(NSStringFromSelector(_cmd));
    
    // 获取旧值
    // kVC
    id oldValue = objc_msgSendSuper(&superStruct, NSSelectorFromString(keyPath));
    
    // 改变父类的值
    objc_msgSendSuper(&superStruct, _cmd, newValue);
    
    NSMutableArray* array = objc_getAssociatedObject(self, kTZKVOAssiociateKey);
    if (array) {
        for (TZInfo* info in array) {
            if ([info.keyPath isEqualToString:keyPath]) {
                info.hanleBlock(info.observer, keyPath, oldValue, newValue);
                return;
            }
        }
    }
}


Class tz_class(id self, SEL _cmd) {
    return class_getSuperclass(object_getClass(self));
}
#pragma mark - 从get方法获取set方法的名称 key ===>>> setKey:
static NSString  * setterForGetter(NSString *getter){
    
    if (getter.length <= 0) { return nil; }
    
    NSString *firstString = [[getter substringToIndex:1] uppercaseString];
    NSString *leaveString = [getter substringFromIndex:1];
    
    return [NSString stringWithFormat:@"set%@%@:",firstString,leaveString];
}

#pragma mark - 从set方法获取getter方法的名称 set:===> Key
static NSString * getterForSetter(NSString *setter){
    
    if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
    
    NSRange range = NSMakeRange(3, setter.length-4);
    NSString *getter = [setter substringWithRange:range];
    NSString *firstString = [[getter substringToIndex:1] lowercaseString];
    getter = [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
    
    return getter;
}

@end