一、 简介
KVO的全称是Key-Value Observing, 翻译过来就是键值监听,可以用于监听某个对象属性值的改变
二、API
相关的API都在NSKeyValueObserving.h这个头文件里
三、常用的API
- 添加观察者:addObserver:forKeyPath:options:context:
- 实现观察相应方法:observeValueForKeyPath:ofObject:change:context:
- 在观察者释放之前移除对象上的监听:removeObserver:forKeyPath:
四、关联
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_p = [Person new];
/**
[_p addObserver:self forKeyPath:@"steps" options:NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew context:nil];
添加观察者后动态生成了Person的子类NSKVONotifying_Person,并动态添加了四个方法
(
"setSteps:",//(Foundation`_NSSetIntValueAndNotify)方法实现为Foundation框架中的_NSSetIntValueAndNotify方法
class,//(Foundation`NSKVOClass)方法实现为Foundation框架中的NSKVOClass方法
dealloc,//(Foundation`NSKVODeallocate)方法实现为Foundation框架中的NSKVODeallocate方法
"_isKVOA"//(Foundation`NSKVOIsAutonotifying)方法实现为Foundation框架中的NSKVOIsAutonotifying方法
)
*/
///添加观察者
[_p addObserver:self forKeyPath:@"fullName" options:NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew context:nil];
}
// 实现监听方法
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@", change);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
_p.firstName = @"123456";
// [_p willChangeValueForKey:@"firstName"];//手动开启
// _p.firstName = @"123456";//关闭自动通知观察者,要想观察到,每次都要调用这对方法
// [_p didChangeValueForKey:@"firstName"];
}
- (void) dealloc {
[_p removeObserver:self forKeyPath:@"fullName"];//移除后isa又指回了父类,生成的子类依然存在
}
@implementation Person
- (NSString*)fullName {
return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName];
}
//关联, lastName 和 firstName任意一个改变都会观察到fullName
+ (NSSet*) keyPathsForValuesAffectingFullName
{
return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}
//关闭自动通知观察者,不手动开启无法通知观察者
+ (BOOL) automaticallyNotifiesObserversOfFirstName {
return NO;
}
/** 过程
NSKeyValueWillChange
[TZPerson setSteps:]
NSKeyValueDidChange
NSKeyValueNotifyObserver
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
*/
//下断点:watchpoint set variable self->_p->_steps
//- (void) setSteps:(int)steps {
// _steps = steps;
//}
@end
/// 打印对应的类及子类
- (void) printClasses:(Class) cls {
/// 注册类的总数
int count = objc_getClassList(NULL, 0);
/// 创建一个数组, 其中包含给定对象
NSMutableArray* array = [NSMutableArray arrayWithObject:cls];
/// 获取所有已注册的类
Class* classes = (Class*)malloc(sizeof(Class)*count);//sizeof(Class)*count:所有注册类的大小
objc_getClassList(classes, count);
/// 遍历s,class_getSuperclass(classes[i]):获取每一个类的父类
for (int i = 0; i < count; i++) {
if (cls == class_getSuperclass(classes[i])) {
[array addObject:classes[i]];
}
}
free(classes);
NSLog(@"classes = %@", array);
}
五、自定义KVO
@interface NSObject (KVO)
// 添加观察者
- (void)gv_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
// 删除观察者
- (void)gv_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
#import <objc/message.h>
@implementation NSObject (KVO)
/// 添加观察者
- (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(@"");
}
#pragma mark - c 函数,ru
static void tz_setter(id self, SEL _cmd, id newValue) {
NSLog(@"%s", __func__);
/**
self:Person
_cmd:"setName:"
newValue:Tom
*/
struct objc_super superStruct = {
self,
class_getSuperclass(object_getClass(self))
};
// 改变父类的值
objc_msgSendSuper(&superStruct, _cmd, newValue);
// 通知观察者, 值发生改变了
// 观察者
id observer = objc_getAssociatedObject(self, (__bridge void *)@"objc");
NSString* setterName = NSStringFromSelector(_cmd);
NSString* key = getterForSetter(setterName);
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{key:newValue}, nil);
}
//销毁执行该方法
Class tz_class(id self, SEL _cmd) {
return class_getSuperclass(object_getClass(self));
}
/// 移除观察者
//- (void)gv_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
//
// // 父类
// Class superClass = [self class];//class_getSuperclass(object_getClass(self));
//// 指回父类
// object_setClass(self, superClass);
//
//}
#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];
NSString *method = [getter capitalizedString];
return [NSString stringWithFormat:@"set%@:",method];
}
#pragma mark - 从set方法获取getter方法的名称 set<Key>:===> 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
添加Blcok
typedef void(^TZKVOBlock)(id observer, NSString* keyPath, id oldValue, id newValue);
@interface NSObject (KVO)
- (void) tz_addObserverBlock:(NSObject*) observer forKeyPath:(NSString*) keyPath handle:(TZKVOBlock) handleBlock;
@end
#import <objc/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];
}
// 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(@"");
}
#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>:===> 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
网友评论