一个对象的属性被观察时系统动态创建了一个子类,并且改变了原有对象的isa
指针指向,指向动态创建的子类,子类中重写了被观察属性的set
方法,在使用点方法和set
方法给属性赋值时,最终调用的是子类中的set
方法。
注意:查看isa
指针指向,如果断点执行过程中isa
指针没有变化, 需要关闭xcode
重新打开
相关代码:在addObserver
处设置断点观察对象isa
指针变化:
- (void)viewDidLoad {
[super viewDidLoad];
People *p = [[People alloc] init];
[p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
p.name = @"123";
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
通过断点观察isa
指针的指向
被观察前isa
指针指向的是原始类如图:
执行代码被观察后指针指向的是NSKVONotifying_People
类:
创建一个分类新增一个方法HBaddObserver
,在方法中创建子类注册并指向子类,再为子类添加set
方法既可。
主要使用函数:
1、创建一个子类
objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
size_t extraBytes)
superclass:设置新类的父类
name:新类名称
extraBytes:额外字节数设置为0
2、注册该类
objc_registerClassPair(Class _Nonnull cls)
cls:当前要注册的类,注册后才可以使用
3、设置当前对象指向其他类
object_setClass(id _Nullable obj, Class _Nonnull cls)
obj:要设置的对象
cls:指向的类
4、动态添加一个方法
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
cls:设置添加方法对应的类
name:选择子(选择器)名称,描述了方法的格式,并不会指向方法
imp:函数名称(函数指针),和选择子一一对应,指向方法实现的地址
通过分类添加新的观察者添加方法:
-(void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
NSString *oldName = NSStringFromClass(self.class);
NSString *newName = [@"HBKVO_" stringByAppendingString:oldName];
//1、创建一个子类
Class newClass = objc_allocateClassPair(self.class, newName.UTF8String, 0);
//2、注册该类
objc_registerClassPair(newClass);
//3、指向子类
object_setClass(self, newClass);
//4、动态添加一个方法
NSString *first = [keyPath substringWithRange:NSMakeRange(0, 1)];
NSString *other = [keyPath substringFromIndex:1];
NSString *setName = [NSString stringWithFormat:@"set%@%@:",first.uppercaseString,other];//设置一个属性名首字母大写的方法
Method method = class_getInstanceMethod(self.class, sel_registerName(setName.UTF8String));
const char *types = method_getTypeEncoding(method);
class_addMethod(newClass, sel_registerName(setName.UTF8String), (IMP)setValue, types);
//class_addMethod(newClass, sel_registerName(setMethod.UTF8String), (IMP)setName, "v@:@");
//设置关联数据
//获取元类旧值使用
objc_setAssociatedObject(self, "keyPath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//设置新值的时候使用
objc_setAssociatedObject(self, "setName", setName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//通知值变化
objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//传进来的内容需要回传
objc_setAssociatedObject(self, "context", (__bridge id _Nullable)(context), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
设置属性值的新调用方法:
void setValue(id self,SEL _cmd,NSString *newValue){
NSLog(@"newValue:%@",newValue);
NSString *keyPath = objc_getAssociatedObject(self, "keyPath");
NSString *setName = objc_getAssociatedObject(self, "setName");
id observer = objc_getAssociatedObject(self, "observer");
id context = objc_getAssociatedObject(self, "context");
//存储新类
Class newClass = [self class];
//指向父类获取旧值
object_setClass(self, class_getSuperclass(newClass));
NSString *oldValue = objc_msgSend(self,sel_registerName(keyPath.UTF8String));
//对原始类属性或成员变量复制
objc_msgSend(self, sel_registerName(setName.UTF8String),newValue);
NSMutableDictionary *change = [NSMutableDictionary dictionary];
if (oldValue) {
change[NSKeyValueChangeOldKey] = oldValue;
}
if (newValue) {
change[NSKeyValueChangeNewKey] = newValue;
}
//调用observer的回调方法
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,observer,change,context);
//操作完成后指回动态创建的新类
object_setClass(self, newClass);
}
通过断点观察isa
指针的指向
被观察前isa
指针指向的是原始类如图:
执行代码被观察后指针指向的是HBKVO_Person
类:
1、主控制器代码如下:
#import "ViewController.h"
#import <objc/message.h>
#import "Person.h"
#import "NSObject+HBKVO.h"
@interface ViewController (){
Person *p;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
p = [[Person alloc] init];
[p HBaddObserver:self forKeyPath:@"Name" options:NSKeyValueObservingOptionNew 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{
static int num = 0;
p.Name = [NSString stringWithFormat:@"%d",num++];
}
@end
2、person
类头文件代码(.m
中无相关代码):
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic,strong)NSString *Name;
@end
NS_ASSUME_NONNULL_END
3、自定义KVO
相关代码(NSObject
分类):
NSObject+HBKVO.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (HBKVO)
- (void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
NS_ASSUME_NONNULL_END
NSObject+HBKVO.m
#import "NSObject+HBKVO.h"
#import <objc/message.h>
@implementation NSObject (HBKVO)
-(void)HBaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
NSString *oldName = NSStringFromClass(self.class);
NSString *newName = [@"HBKVO_" stringByAppendingString:oldName];
//1、创建一个子类
Class newClass = objc_allocateClassPair(self.class, newName.UTF8String, 0);
//2、注册该类
objc_registerClassPair(newClass);
//3、指向子类
object_setClass(self, newClass);
//4、动态添加一个方法
NSString *first = [keyPath substringWithRange:NSMakeRange(0, 1)];
NSString *other = [keyPath substringFromIndex:1];
NSString *setName = [NSString stringWithFormat:@"set%@%@:",first.uppercaseString,other];//设置一个属性名首字母大写的方法
Method method = class_getInstanceMethod(self.class, sel_registerName(setName.UTF8String));
const char *types = method_getTypeEncoding(method);
class_addMethod(newClass, sel_registerName(setName.UTF8String), (IMP)setValue, types);
//class_addMethod(newClass, sel_registerName(setMethod.UTF8String), (IMP)setName, "v@:@");
//设置关联数据
//获取元类旧值使用
objc_setAssociatedObject(self, "keyPath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//设置新值的时候使用
objc_setAssociatedObject(self, "setName", setName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//通知值变化
objc_setAssociatedObject(self, "observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//传进来的内容需要回传
objc_setAssociatedObject(self, "context", (__bridge id _Nullable)(context), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
void setValue(id self,SEL _cmd,NSString *newValue){
NSLog(@"newValue:%@",newValue);
NSString *keyPath = objc_getAssociatedObject(self, "keyPath");
NSString *setName = objc_getAssociatedObject(self, "setName");
id observer = objc_getAssociatedObject(self, "observer");
id context = objc_getAssociatedObject(self, "context");
//存储新类
Class newClass = [self class];
//指向父类获取旧值
object_setClass(self, class_getSuperclass(newClass));
NSString *oldValue = objc_msgSend(self,sel_registerName(keyPath.UTF8String));
//对原始类属性或成员变量复制
objc_msgSend(self, sel_registerName(setName.UTF8String),newValue);
NSMutableDictionary *change = [NSMutableDictionary dictionary];
if (oldValue) {
change[NSKeyValueChangeOldKey] = oldValue;
}
if (newValue) {
change[NSKeyValueChangeNewKey] = newValue;
}
//调用observer的回调方法
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),keyPath,observer,change,context);
//操作完成后指回动态创建的新类
object_setClass(self, newClass);
}
@end