@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@interface Person (KVO)
- (void)by_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context;
@end
@implementation Person (KVO)
- (void)by_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
NSString *originClassName = NSStringFromClass(self.class);
NSString *newClassName = [@"CJ_NSKVONotifying_" stringByAppendingString:originClassName];
Class kvoClass = objc_allocateClassPair([self class], [newClassName UTF8String], 0);
class_addMethod(kvoClass, @selector(setName:), (IMP)setName, "v@:@");
objc_registerClassPair(kvoClass);
object_setClass(self, kvoClass);
objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
void setName(id self, SEL _cmd, NSString *name) {
Class kvoClass = [self class];
object_setClass(self, class_getSuperclass([self class]));
objc_msgSend(self, @selector(setName:), name);
id objc = objc_getAssociatedObject(self, @"observer");
NSDictionary <NSKeyValueChangeKey, id>*change = @{NSKeyValueChangeKindKey: @1,
NSKeyValueChangeNewKey:name};
objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:), @"name",self, change,nil);
object_setClass(self, kvoClass);
}
@end
Person *personModel = [[Person alloc] init];
personModel.name = @"Cocoa";
NSLog(@"%@",object_getClass(personModel));
[personModel by_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld context:nil];
NSLog(@"%@",object_getClass(personModel));
personModel.name = @"Cocoa Jason";
2022-03-03 10:26:52.096757+0800 dsadasdasdsa[10757:17330187] Person
2022-03-03 10:26:52.096907+0800 dsadasdasdsa[10757:17330187] CJ_NSKVONotifying_Person
2022-03-03 10:26:52.097098+0800 dsadasdasdsa[10757:17330187] {
kind = 1;
new = "Cocoa Jason";
}