KVO
KVO基本使用
- KVO的全称是Key-Value Observing, 翻译过来就是键值监听,可以用于监听某个对象属性值的改变
- 相关的API都在NSKeyValueObserving.h这个头文件里
- 常用的API
-
添加观察者:addObserver:forKeyPath:options:context:
-
实现观察相应方法:observeValueForKeyPath:ofObject:change:context:
-
在观察者释放之前移除对象上的监听:removeObserver:forKeyPath:
@interface TZPerson : NSObject // 步数 @property (nonatomic, assign) int steps; // 依赖关系的成员 @property (nonatomic, strong) NSString* fullName; @property (nonatomic, strong) NSString* firstName; @property (nonatomic, strong) NSString* lastName; @end @implementation TZPerson - (NSString*)fullName { return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName]; } + (NSSet*) keyPathsForValuesAffectingFullName { return [NSSet setWithObjects:@"lastName", @"firstName", nil]; } //改为手动监听 + (BOOL) automaticallyNotifiesObserversOfSteps { return NO; } @end
@interface TZSecondViewController () @property (nonatomic, strong) TZPerson* p; @end @implementation TZSecondViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. _p = [TZPerson new]; /// 添加观察者 [_p addObserver:self forKeyPath:@"steps" 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 willChangeValueForKey:@"steps"]; _p.steps++; [_p didChangeValueForKey:@"steps"]; // _p.firstName = @"三"; } - (void) dealloc { [_p removeObserver:self forKeyPath:@"steps"]; } @end
-
KVO原理分析
#import "TZSecondViewController.h"
#import "TZPerson.h"
#import <objc/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自定义
- runtime方法调用本质,实质就是objc_megSend。
- 找方法实现的过程:给实例发送消息通过isa找到类。从类里面找缓存、方法列表里面找
-
KVO未被观察。通过isa找到类对象,从缓存和方法列表里面找
-
KVO被观察。通过isa找runtime动态生成的NSKVONotifying_className子类对象,从缓存和方法列表里面找,调用的方法是子类的,子类的方法实现是调用了foundation框架_NSSetIntValueAndNotify方法的,方法里面做了两件事,改变父类值,通知观察者,移除操作,isa指回来
#import "NSObject+KVO.h" #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); } return newClass; } #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)) }; // 改变父类的值 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]; } #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
KVO延伸
自动销毁机制
-
自动销毁机制
// 添加析构方法 SEL deallocSEL = NSSelectorFromString(@"dealloc"); Method deallocMethod = class_getInstanceMethod([self class], deallocSEL); const char* deallocTypes = method_getTypeEncoding(deallocMethod); class_addMethod(newClass, deallocSEL, (IMP)myDealloc, deallocTypes); void myDealloc(id self, SEL _cmd) { // 父类 Class superClass = [self class];//class_getSuperclass(object_getClass(self)); object_setClass(self, superClass); }
Blocks
#import "NSObject+KVO.h"
#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];
}
/// 添加观察者
- (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