-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context;
//
// Person.h
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 iThinkerYZ. All rights reserved.
//
#import
@interface Person : NSObject
@property (nonatomic , assign) NSInteger age ;
@property (nonatomic , copy) NSString * name ;
@end
//
// Person.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 iThinkerYZ. All rights reserved.
//
#import "Person.h"
@implementation Person
@end
#import
@interface ViewController : UIViewController
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic , strong) Person * person ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
self.person.age = 1 ;
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)dealloc
{
[self.person removeObserver:self forKeyPath:@"age"];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 实际调用了 setAge: 方法
self.person.age++ ;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context
{
NSLog(@"最新年龄:%ld , 类名:%@",self.person.age,NSStringFromClass([self.person class]));
}
@end
把 Person 对象的 age 属性(默认是 @protected 妨问权限)转变成 @public 的权限
//
// Person.h
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import
@interface Person : NSObject
{
// 默认是私有变量,把它变成公共变量,外面就可以用 ->_age 这样的格式调用该对象属性,不然会报错
@public
NSInteger _age ;
}
@property (nonatomic , assign) NSInteger age ;
@property (nonatomic , copy) NSString * name ;
@end
//
// Person.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import "Person.h"
@implementation Person
@end
在 ViewController 中注意第 46 行代码的使用如下
//
// ViewController.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import "ViewController.h"
#import "Person.h"
#import "NSObject+KVO.h"
@interface ViewController ()
@property (nonatomic , strong) Person * person ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
self.person.age = 1 ;
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)dealloc
{
[self.person removeObserver:self forKeyPath:@"age"];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// // 实际调用了 setAge: 方法
// self.person.age++ ;
// 实际就只是单纯的赋值
self.person->_age++ ;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context
{
NSLog(@"最新年龄:%ld , 类名:%@",self.person.age,NSStringFromClass([self.person class]));
}
@end
结果却是不会调下面的方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context
{
NSLog(@"最新年龄:%ld , 类名:%@",self.person.age,NSStringFromClass([self.person class]));
}
以上面的代码为例,系统KVO的实现过程
实现自定义KVO
Person 类
//
// Person.h
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import
@interface Person : NSObject
@property (nonatomic , assign) NSInteger age ;
@property (nonatomic , copy) NSString * name ;
@end
//
// Person.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import "Person.h"
@implementation Person
@end
创建 NSObject 分类 NSObject+KVO 实现添加观察者方法:
//
// NSObject+KVO.h
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import
@interface NSObject (KVO)
// 添加一个前缀为区分与系统不一样方法
-(void)qj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context ;
@end
实现 NSObject+KVO 对象方法
//
// NSObject+KVO.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import "NSObject+KVO.h"
// runtime 需要的头文件
#import
@implementation NSObject (KVO)
-(void)qj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
// 1. 设置该方法的 当前对象的 isa 指针指向动态创建的 QJKVONotifying_Person 类
Class subClass = [self createClassWithClassName:"QJKVONotifying_Person" superClassName:[self class]];
object_setClass(self, subClass);
// 2. 动态添加属性 observer 、keyPath 等
// OBJC_ASSOCIATION_RETAIN_NONATOMIC 即在 RAC 环境下等同于 @property (nonatomic , strong) [observer class] * observer ;
objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, "keyPath", keyPath, OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, "options", @(options), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, "context", (__bridge id)(context), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 3. 方法交换
// 3.1 获取属性的 setter 方法名
NSString * methodName = [NSString stringWithFormat:@"set%@%@:",[[keyPath substringToIndex:1] uppercaseString],[keyPath substringFromIndex:1]];
// 3.2 跟据方法名 获取 方法
Method InstanceMethod1 = class_getInstanceMethod([self class], NSSelectorFromString(methodName));
// 3.3 把 setAgeMethod: 方法包装成 Method 类型
Method InstanceMethod2 = class_getInstanceMethod([self class], @selector(setAgeMethod:));
// 3.4 两个方法交换
method_exchangeImplementations(InstanceMethod1, InstanceMethod2);
}
-(void)setAgeMethod:(NSInteger)age
{
// 1.取出之前动态添加属性对应的值
NSObject * observer = objc_getAssociatedObject(self, "observer");
NSString * keyPath = objc_getAssociatedObject(self, "keyPath");
void * context = (__bridge void *)(objc_getAssociatedObject(self, "context"));
NSKeyValueObservingOptions options = [objc_getAssociatedObject(self, @"options") integerValue];
// 2.取出 keyPath 先前的值
NSInteger oldAge = [[self valueForKeyPath:keyPath] integerValue];
// 3.因为 setAgeMethod: 方法 与 方法名为 methodName 的方法交换了,所以不会告成循环
[self setAgeMethod:age];
// 4. 调用 KVO 监听的方法
if ([observer respondsToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]) {
NSMutableDictionary * changes = [NSMutableDictionary dictionary];
[changes setValue:keyPath forKey:@"keyPath"];
// 4.1通过操类型来设置字典的值
if (options == NSKeyValueObservingOptionOld) {
[changes setValue:@(oldAge) forKey:NSKeyValueChangeOldKey];
}
else if (options == NSKeyValueObservingOptionNew){
[changes setValue:@([[self valueForKeyPath:keyPath] integerValue]) forKey:NSKeyValueChangeNewKey];
}// 接下来的枚举就不再写了
// 4.2 调用 observer 观察者实现的 KVO 方法
[observer observeValueForKeyPath:keyPath ofObject:self change:changes context:context];
}
}
/**
跟据类名 className 和 父类名 superClassName 创建名为 className 的类
*/
-(Class)createClassWithClassName:(const char * )className superClass:(Class)superClass
{
// 1.先查找项目中是否存在这个类
Class subClass = objc_getClass(className);
// 2.如果不存在则动态创建
if (!subClass)
{
// 2.1 根据父类来创建当前的子类
subClass = objc_allocateClassPair(superClass, className, 0);
}
return subClass ;
}
@end
在 ViewController 中的用法如下
//
// ViewController.m
//
// Created by 瞿杰 on 2017/8/16.
// Copyright © 2017年 qujie. All rights reserved.
//
#import "ViewController.h"
#import "Person.h"
#import "NSObject+KVO.h"
@interface ViewController ()
@property (nonatomic , strong) Person * person ;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
self.person.age = 1 ;
// [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
[self.person qj_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)dealloc
{
[self.person removeObserver:self forKeyPath:@"age"];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 实际调用了 setAge: 方法
self.person.age++ ;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void *)context
{
NSLog(@"最新年龄:%ld , 类名:%@",self.person.age,NSStringFromClass([self.person class]));
}
@end